mirror of
https://github.com/id-Software/DOOM-3.git
synced 2026-03-19 16:39:27 +01:00
hello world
This commit is contained in:
1774
neo/renderer/Cinematic.cpp
Normal file
1774
neo/renderer/Cinematic.cpp
Normal file
File diff suppressed because it is too large
Load Diff
114
neo/renderer/Cinematic.h
Normal file
114
neo/renderer/Cinematic.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __CINEMATIC_H__
|
||||
#define __CINEMATIC_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
RoQ cinematic
|
||||
|
||||
Multiple idCinematics can run simultaniously.
|
||||
A single idCinematic can be reused for multiple files if desired.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
// cinematic states
|
||||
typedef enum {
|
||||
FMV_IDLE,
|
||||
FMV_PLAY, // play
|
||||
FMV_EOF, // all other conditions, i.e. stop/EOF/abort
|
||||
FMV_ID_BLT,
|
||||
FMV_ID_IDLE,
|
||||
FMV_LOOPED,
|
||||
FMV_ID_WAIT
|
||||
} cinStatus_t;
|
||||
|
||||
// a cinematic stream generates an image buffer, which the caller will upload to a texture
|
||||
typedef struct {
|
||||
int imageWidth, imageHeight; // will be a power of 2
|
||||
const byte * image; // RGBA format, alpha will be 255
|
||||
int status;
|
||||
} cinData_t;
|
||||
|
||||
class idCinematic {
|
||||
public:
|
||||
// initialize cinematic play back data
|
||||
static void InitCinematic( void );
|
||||
|
||||
// shutdown cinematic play back data
|
||||
static void ShutdownCinematic( void );
|
||||
|
||||
// allocates and returns a private subclass that implements the methods
|
||||
// This should be used instead of new
|
||||
static idCinematic *Alloc();
|
||||
|
||||
// frees all allocated memory
|
||||
virtual ~idCinematic();
|
||||
|
||||
// returns false if it failed to load
|
||||
virtual bool InitFromFile( const char *qpath, bool looping );
|
||||
|
||||
// returns the length of the animation in milliseconds
|
||||
virtual int AnimationLength();
|
||||
|
||||
// the pointers in cinData_t will remain valid until the next UpdateForTime() call
|
||||
virtual cinData_t ImageForTime( int milliseconds );
|
||||
|
||||
// closes the file and frees all allocated memory
|
||||
virtual void Close();
|
||||
|
||||
// closes the file and frees all allocated memory
|
||||
virtual void ResetTime(int time);
|
||||
};
|
||||
|
||||
/*
|
||||
===============================================
|
||||
|
||||
Sound meter.
|
||||
|
||||
===============================================
|
||||
*/
|
||||
|
||||
class idSndWindow : public idCinematic {
|
||||
public:
|
||||
|
||||
idSndWindow() { showWaveform = false; }
|
||||
~idSndWindow() {}
|
||||
|
||||
bool InitFromFile( const char *qpath, bool looping );
|
||||
cinData_t ImageForTime( int milliseconds );
|
||||
int AnimationLength();
|
||||
|
||||
private:
|
||||
bool showWaveform;
|
||||
};
|
||||
|
||||
#endif /* !__CINEMATIC_H__ */
|
||||
652
neo/renderer/GuiModel.cpp
Normal file
652
neo/renderer/GuiModel.cpp
Normal file
@@ -0,0 +1,652 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
idGuiModel::idGuiModel
|
||||
================
|
||||
*/
|
||||
idGuiModel::idGuiModel() {
|
||||
indexes.SetGranularity( 1000 );
|
||||
verts.SetGranularity( 1000 );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idGuiModel::Clear
|
||||
|
||||
Begins collecting draw commands into surfaces
|
||||
================
|
||||
*/
|
||||
void idGuiModel::Clear() {
|
||||
surfaces.SetNum( 0, false );
|
||||
indexes.SetNum( 0, false );
|
||||
verts.SetNum( 0, false );
|
||||
AdvanceSurf();
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idGuiModel::WriteToDemo
|
||||
================
|
||||
*/
|
||||
void idGuiModel::WriteToDemo( idDemoFile *demo ) {
|
||||
int i, j;
|
||||
|
||||
i = verts.Num();
|
||||
demo->WriteInt( i );
|
||||
for ( j = 0; j < i; j++ )
|
||||
{
|
||||
demo->WriteVec3( verts[j].xyz );
|
||||
demo->WriteVec2( verts[j].st );
|
||||
demo->WriteVec3( verts[j].normal );
|
||||
demo->WriteVec3( verts[j].tangents[0] );
|
||||
demo->WriteVec3( verts[j].tangents[1] );
|
||||
demo->WriteUnsignedChar( verts[j].color[0] );
|
||||
demo->WriteUnsignedChar( verts[j].color[1] );
|
||||
demo->WriteUnsignedChar( verts[j].color[2] );
|
||||
demo->WriteUnsignedChar( verts[j].color[3] );
|
||||
}
|
||||
|
||||
i = indexes.Num();
|
||||
demo->WriteInt( i );
|
||||
for ( j = 0; j < i; j++ ) {
|
||||
demo->WriteInt(indexes[j] );
|
||||
}
|
||||
|
||||
i = surfaces.Num();
|
||||
demo->WriteInt( i );
|
||||
for ( j = 0 ; j < i ; j++ ) {
|
||||
guiModelSurface_t *surf = &surfaces[j];
|
||||
|
||||
demo->WriteInt( (int&)surf->material );
|
||||
demo->WriteFloat( surf->color[0] );
|
||||
demo->WriteFloat( surf->color[1] );
|
||||
demo->WriteFloat( surf->color[2] );
|
||||
demo->WriteFloat( surf->color[3] );
|
||||
demo->WriteInt( surf->firstVert );
|
||||
demo->WriteInt( surf->numVerts );
|
||||
demo->WriteInt( surf->firstIndex );
|
||||
demo->WriteInt( surf->numIndexes );
|
||||
demo->WriteHashString( surf->material->GetName() );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idGuiModel::ReadFromDemo
|
||||
================
|
||||
*/
|
||||
void idGuiModel::ReadFromDemo( idDemoFile *demo ) {
|
||||
int i, j;
|
||||
|
||||
i = verts.Num();
|
||||
demo->ReadInt( i );
|
||||
verts.SetNum( i, false );
|
||||
for ( j = 0; j < i; j++ )
|
||||
{
|
||||
demo->ReadVec3( verts[j].xyz );
|
||||
demo->ReadVec2( verts[j].st );
|
||||
demo->ReadVec3( verts[j].normal );
|
||||
demo->ReadVec3( verts[j].tangents[0] );
|
||||
demo->ReadVec3( verts[j].tangents[1] );
|
||||
demo->ReadUnsignedChar( verts[j].color[0] );
|
||||
demo->ReadUnsignedChar( verts[j].color[1] );
|
||||
demo->ReadUnsignedChar( verts[j].color[2] );
|
||||
demo->ReadUnsignedChar( verts[j].color[3] );
|
||||
}
|
||||
|
||||
i = indexes.Num();
|
||||
demo->ReadInt( i );
|
||||
indexes.SetNum( i, false );
|
||||
for ( j = 0; j < i; j++ ) {
|
||||
demo->ReadInt(indexes[j] );
|
||||
}
|
||||
|
||||
i = surfaces.Num();
|
||||
demo->ReadInt( i );
|
||||
surfaces.SetNum( i, false );
|
||||
for ( j = 0 ; j < i ; j++ ) {
|
||||
guiModelSurface_t *surf = &surfaces[j];
|
||||
|
||||
demo->ReadInt( (int&)surf->material );
|
||||
demo->ReadFloat( surf->color[0] );
|
||||
demo->ReadFloat( surf->color[1] );
|
||||
demo->ReadFloat( surf->color[2] );
|
||||
demo->ReadFloat( surf->color[3] );
|
||||
demo->ReadInt( surf->firstVert );
|
||||
demo->ReadInt( surf->numVerts );
|
||||
demo->ReadInt( surf->firstIndex );
|
||||
demo->ReadInt( surf->numIndexes );
|
||||
surf->material = declManager->FindMaterial( demo->ReadHashString() );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
EmitSurface
|
||||
================
|
||||
*/
|
||||
void idGuiModel::EmitSurface( guiModelSurface_t *surf, float modelMatrix[16], float modelViewMatrix[16], bool depthHack ) {
|
||||
srfTriangles_t *tri;
|
||||
|
||||
if ( surf->numVerts == 0 ) {
|
||||
return; // nothing in the surface
|
||||
}
|
||||
|
||||
// copy verts and indexes
|
||||
tri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *tri ) );
|
||||
|
||||
tri->numIndexes = surf->numIndexes;
|
||||
tri->numVerts = surf->numVerts;
|
||||
tri->indexes = (glIndex_t *)R_FrameAlloc( tri->numIndexes * sizeof( tri->indexes[0] ) );
|
||||
memcpy( tri->indexes, &indexes[surf->firstIndex], tri->numIndexes * sizeof( tri->indexes[0] ) );
|
||||
|
||||
// we might be able to avoid copying these and just let them reference the list vars
|
||||
// but some things, like deforms and recursive
|
||||
// guis, need to access the verts in cpu space, not just through the vertex range
|
||||
tri->verts = (idDrawVert *)R_FrameAlloc( tri->numVerts * sizeof( tri->verts[0] ) );
|
||||
memcpy( tri->verts, &verts[surf->firstVert], tri->numVerts * sizeof( tri->verts[0] ) );
|
||||
|
||||
// move the verts to the vertex cache
|
||||
tri->ambientCache = vertexCache.AllocFrameTemp( tri->verts, tri->numVerts * sizeof( tri->verts[0] ) );
|
||||
|
||||
// if we are out of vertex cache, don't create the surface
|
||||
if ( !tri->ambientCache ) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderEntity_t renderEntity;
|
||||
memset( &renderEntity, 0, sizeof( renderEntity ) );
|
||||
memcpy( renderEntity.shaderParms, surf->color, sizeof( surf->color ) );
|
||||
|
||||
viewEntity_t *guiSpace = (viewEntity_t *)R_ClearedFrameAlloc( sizeof( *guiSpace ) );
|
||||
memcpy( guiSpace->modelMatrix, modelMatrix, sizeof( guiSpace->modelMatrix ) );
|
||||
memcpy( guiSpace->modelViewMatrix, modelViewMatrix, sizeof( guiSpace->modelViewMatrix ) );
|
||||
guiSpace->weaponDepthHack = depthHack;
|
||||
|
||||
// add the surface, which might recursively create another gui
|
||||
R_AddDrawSurf( tri, guiSpace, &renderEntity, surf->material, tr.viewDef->scissor );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
EmitToCurrentView
|
||||
====================
|
||||
*/
|
||||
void idGuiModel::EmitToCurrentView( float modelMatrix[16], bool depthHack ) {
|
||||
float modelViewMatrix[16];
|
||||
|
||||
myGlMultMatrix( modelMatrix, tr.viewDef->worldSpace.modelViewMatrix,
|
||||
modelViewMatrix );
|
||||
|
||||
for ( int i = 0 ; i < surfaces.Num() ; i++ ) {
|
||||
EmitSurface( &surfaces[i], modelMatrix, modelViewMatrix, depthHack );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idGuiModel::EmitFullScreen
|
||||
|
||||
Creates a view that covers the screen and emit the surfaces
|
||||
================
|
||||
*/
|
||||
void idGuiModel::EmitFullScreen( void ) {
|
||||
viewDef_t *viewDef;
|
||||
|
||||
if ( surfaces[0].numVerts == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewDef = (viewDef_t *)R_ClearedFrameAlloc( sizeof( *viewDef ) );
|
||||
|
||||
// for gui editor
|
||||
if ( !tr.viewDef || !tr.viewDef->isEditor ) {
|
||||
viewDef->renderView.x = 0;
|
||||
viewDef->renderView.y = 0;
|
||||
viewDef->renderView.width = SCREEN_WIDTH;
|
||||
viewDef->renderView.height = SCREEN_HEIGHT;
|
||||
|
||||
tr.RenderViewToViewport( &viewDef->renderView, &viewDef->viewport );
|
||||
|
||||
viewDef->scissor.x1 = 0;
|
||||
viewDef->scissor.y1 = 0;
|
||||
viewDef->scissor.x2 = viewDef->viewport.x2 - viewDef->viewport.x1;
|
||||
viewDef->scissor.y2 = viewDef->viewport.y2 - viewDef->viewport.y1;
|
||||
} else {
|
||||
viewDef->renderView.x = tr.viewDef->renderView.x;
|
||||
viewDef->renderView.y = tr.viewDef->renderView.y;
|
||||
viewDef->renderView.width = tr.viewDef->renderView.width;
|
||||
viewDef->renderView.height = tr.viewDef->renderView.height;
|
||||
|
||||
viewDef->viewport.x1 = tr.viewDef->renderView.x;
|
||||
viewDef->viewport.x2 = tr.viewDef->renderView.x + tr.viewDef->renderView.width;
|
||||
viewDef->viewport.y1 = tr.viewDef->renderView.y;
|
||||
viewDef->viewport.y2 = tr.viewDef->renderView.y + tr.viewDef->renderView.height;
|
||||
|
||||
viewDef->scissor.x1 = tr.viewDef->scissor.x1;
|
||||
viewDef->scissor.y1 = tr.viewDef->scissor.y1;
|
||||
viewDef->scissor.x2 = tr.viewDef->scissor.x2;
|
||||
viewDef->scissor.y2 = tr.viewDef->scissor.y2;
|
||||
}
|
||||
|
||||
viewDef->floatTime = tr.frameShaderTime;
|
||||
|
||||
// qglOrtho( 0, 640, 480, 0, 0, 1 ); // always assume 640x480 virtual coordinates
|
||||
viewDef->projectionMatrix[0] = 2.0f / 640.0f;
|
||||
viewDef->projectionMatrix[5] = -2.0f / 480.0f;
|
||||
viewDef->projectionMatrix[10] = -2.0f / 1.0f;
|
||||
viewDef->projectionMatrix[12] = -1.0f;
|
||||
viewDef->projectionMatrix[13] = 1.0f;
|
||||
viewDef->projectionMatrix[14] = -1.0f;
|
||||
viewDef->projectionMatrix[15] = 1.0f;
|
||||
|
||||
viewDef->worldSpace.modelViewMatrix[0] = 1.0f;
|
||||
viewDef->worldSpace.modelViewMatrix[5] = 1.0f;
|
||||
viewDef->worldSpace.modelViewMatrix[10] = 1.0f;
|
||||
viewDef->worldSpace.modelViewMatrix[15] = 1.0f;
|
||||
|
||||
viewDef->maxDrawSurfs = surfaces.Num();
|
||||
viewDef->drawSurfs = (drawSurf_t **)R_FrameAlloc( viewDef->maxDrawSurfs * sizeof( viewDef->drawSurfs[0] ) );
|
||||
viewDef->numDrawSurfs = 0;
|
||||
|
||||
viewDef_t *oldViewDef = tr.viewDef;
|
||||
tr.viewDef = viewDef;
|
||||
|
||||
// add the surfaces to this view
|
||||
for ( int i = 0 ; i < surfaces.Num() ; i++ ) {
|
||||
EmitSurface( &surfaces[i], viewDef->worldSpace.modelMatrix, viewDef->worldSpace.modelViewMatrix, false );
|
||||
}
|
||||
|
||||
tr.viewDef = oldViewDef;
|
||||
|
||||
// add the command to draw this view
|
||||
R_AddDrawViewCmd( viewDef );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
AdvanceSurf
|
||||
=============
|
||||
*/
|
||||
void idGuiModel::AdvanceSurf() {
|
||||
guiModelSurface_t s;
|
||||
|
||||
if ( surfaces.Num() ) {
|
||||
s.color[0] = surf->color[0];
|
||||
s.color[1] = surf->color[1];
|
||||
s.color[2] = surf->color[2];
|
||||
s.color[3] = surf->color[3];
|
||||
s.material = surf->material;
|
||||
} else {
|
||||
s.color[0] = 1;
|
||||
s.color[1] = 1;
|
||||
s.color[2] = 1;
|
||||
s.color[3] = 1;
|
||||
s.material = tr.defaultMaterial;
|
||||
}
|
||||
s.numIndexes = 0;
|
||||
s.firstIndex = indexes.Num();
|
||||
s.numVerts = 0;
|
||||
s.firstVert = verts.Num();
|
||||
|
||||
surfaces.Append( s );
|
||||
surf = &surfaces[ surfaces.Num() - 1 ];
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
SetColor
|
||||
=============
|
||||
*/
|
||||
void idGuiModel::SetColor( float r, float g, float b, float a ) {
|
||||
if ( !glConfig.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
if ( r == surf->color[0] && g == surf->color[1]
|
||||
&& b == surf->color[2] && a == surf->color[3] ) {
|
||||
return; // no change
|
||||
}
|
||||
|
||||
if ( surf->numVerts ) {
|
||||
AdvanceSurf();
|
||||
}
|
||||
|
||||
// change the parms
|
||||
surf->color[0] = r;
|
||||
surf->color[1] = g;
|
||||
surf->color[2] = b;
|
||||
surf->color[3] = a;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
DrawStretchPic
|
||||
=============
|
||||
*/
|
||||
void idGuiModel::DrawStretchPic( const idDrawVert *dverts, const glIndex_t *dindexes, int vertCount, int indexCount, const idMaterial *hShader,
|
||||
bool clip, float min_x, float min_y, float max_x, float max_y ) {
|
||||
if ( !glConfig.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
if ( !( dverts && dindexes && vertCount && indexCount && hShader ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// break the current surface if we are changing to a new material
|
||||
if ( hShader != surf->material ) {
|
||||
if ( surf->numVerts ) {
|
||||
AdvanceSurf();
|
||||
}
|
||||
const_cast<idMaterial *>(hShader)->EnsureNotPurged(); // in case it was a gui item started before a level change
|
||||
surf->material = hShader;
|
||||
}
|
||||
|
||||
// add the verts and indexes to the current surface
|
||||
|
||||
if ( clip ) {
|
||||
int i, j;
|
||||
|
||||
// FIXME: this is grim stuff, and should be rewritten if we have any significant
|
||||
// number of guis asking for clipping
|
||||
idFixedWinding w;
|
||||
for ( i = 0; i < indexCount; i += 3 ) {
|
||||
w.Clear();
|
||||
w.AddPoint(idVec5(dverts[dindexes[i]].xyz.x, dverts[dindexes[i]].xyz.y, dverts[dindexes[i]].xyz.z, dverts[dindexes[i]].st.x, dverts[dindexes[i]].st.y));
|
||||
w.AddPoint(idVec5(dverts[dindexes[i+1]].xyz.x, dverts[dindexes[i+1]].xyz.y, dverts[dindexes[i+1]].xyz.z, dverts[dindexes[i+1]].st.x, dverts[dindexes[i+1]].st.y));
|
||||
w.AddPoint(idVec5(dverts[dindexes[i+2]].xyz.x, dverts[dindexes[i+2]].xyz.y, dverts[dindexes[i+2]].xyz.z, dverts[dindexes[i+2]].st.x, dverts[dindexes[i+2]].st.y));
|
||||
|
||||
for ( j = 0; j < 3; j++ ) {
|
||||
if ( w[j].x < min_x || w[j].x > max_x ||
|
||||
w[j].y < min_y || w[j].y > max_y ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j < 3 ) {
|
||||
idPlane p;
|
||||
p.Normal().y = p.Normal().z = 0.0f; p.Normal().x = 1.0f; p.SetDist( min_x );
|
||||
w.ClipInPlace( p );
|
||||
p.Normal().y = p.Normal().z = 0.0f; p.Normal().x = -1.0f; p.SetDist( -max_x );
|
||||
w.ClipInPlace( p );
|
||||
p.Normal().x = p.Normal().z = 0.0f; p.Normal().y = 1.0f; p.SetDist( min_y );
|
||||
w.ClipInPlace( p );
|
||||
p.Normal().x = p.Normal().z = 0.0f; p.Normal().y = -1.0f; p.SetDist( -max_y );
|
||||
w.ClipInPlace( p );
|
||||
}
|
||||
|
||||
int numVerts = verts.Num();
|
||||
verts.SetNum( numVerts + w.GetNumPoints(), false );
|
||||
for ( j = 0 ; j < w.GetNumPoints() ; j++ ) {
|
||||
idDrawVert *dv = &verts[numVerts+j];
|
||||
|
||||
dv->xyz.x = w[j].x;
|
||||
dv->xyz.y = w[j].y;
|
||||
dv->xyz.z = w[j].z;
|
||||
dv->st.x = w[j].s;
|
||||
dv->st.y = w[j].t;
|
||||
dv->normal.Set(0, 0, 1);
|
||||
dv->tangents[0].Set(1, 0, 0);
|
||||
dv->tangents[1].Set(0, 1, 0);
|
||||
}
|
||||
surf->numVerts += w.GetNumPoints();
|
||||
|
||||
for ( j = 2; j < w.GetNumPoints(); j++ ) {
|
||||
indexes.Append( numVerts - surf->firstVert );
|
||||
indexes.Append( numVerts + j - 1 - surf->firstVert );
|
||||
indexes.Append( numVerts + j - surf->firstVert );
|
||||
surf->numIndexes += 3;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
int numVerts = verts.Num();
|
||||
int numIndexes = indexes.Num();
|
||||
|
||||
verts.AssureSize( numVerts + vertCount );
|
||||
indexes.AssureSize( numIndexes + indexCount );
|
||||
|
||||
surf->numVerts += vertCount;
|
||||
surf->numIndexes += indexCount;
|
||||
|
||||
for ( int i = 0; i < indexCount; i++ ) {
|
||||
indexes[numIndexes + i] = numVerts + dindexes[i] - surf->firstVert;
|
||||
}
|
||||
|
||||
memcpy( &verts[numVerts], dverts, vertCount * sizeof( verts[0] ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
DrawStretchPic
|
||||
|
||||
x/y/w/h are in the 0,0 to 640,480 range
|
||||
=============
|
||||
*/
|
||||
void idGuiModel::DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial *hShader ) {
|
||||
idDrawVert verts[4];
|
||||
glIndex_t indexes[6];
|
||||
|
||||
if ( !glConfig.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
if ( !hShader ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// clip to edges, because the pic may be going into a guiShader
|
||||
// instead of full screen
|
||||
if ( x < 0 ) {
|
||||
s1 += ( s2 - s1 ) * -x / w;
|
||||
w += x;
|
||||
x = 0;
|
||||
}
|
||||
if ( y < 0 ) {
|
||||
t1 += ( t2 - t1 ) * -y / h;
|
||||
h += y;
|
||||
y = 0;
|
||||
}
|
||||
if ( x + w > 640 ) {
|
||||
s2 -= ( s2 - s1 ) * ( x + w - 640 ) / w;
|
||||
w = 640 - x;
|
||||
}
|
||||
if ( y + h > 480 ) {
|
||||
t2 -= ( t2 - t1 ) * ( y + h - 480 ) / h;
|
||||
h = 480 - y;
|
||||
}
|
||||
|
||||
if ( w <= 0 || h <= 0 ) {
|
||||
return; // completely clipped away
|
||||
}
|
||||
|
||||
indexes[0] = 3;
|
||||
indexes[1] = 0;
|
||||
indexes[2] = 2;
|
||||
indexes[3] = 2;
|
||||
indexes[4] = 0;
|
||||
indexes[5] = 1;
|
||||
verts[0].xyz[0] = x;
|
||||
verts[0].xyz[1] = y;
|
||||
verts[0].xyz[2] = 0;
|
||||
verts[0].st[0] = s1;
|
||||
verts[0].st[1] = t1;
|
||||
verts[0].normal[0] = 0;
|
||||
verts[0].normal[1] = 0;
|
||||
verts[0].normal[2] = 1;
|
||||
verts[0].tangents[0][0] = 1;
|
||||
verts[0].tangents[0][1] = 0;
|
||||
verts[0].tangents[0][2] = 0;
|
||||
verts[0].tangents[1][0] = 0;
|
||||
verts[0].tangents[1][1] = 1;
|
||||
verts[0].tangents[1][2] = 0;
|
||||
verts[1].xyz[0] = x + w;
|
||||
verts[1].xyz[1] = y;
|
||||
verts[1].xyz[2] = 0;
|
||||
verts[1].st[0] = s2;
|
||||
verts[1].st[1] = t1;
|
||||
verts[1].normal[0] = 0;
|
||||
verts[1].normal[1] = 0;
|
||||
verts[1].normal[2] = 1;
|
||||
verts[1].tangents[0][0] = 1;
|
||||
verts[1].tangents[0][1] = 0;
|
||||
verts[1].tangents[0][2] = 0;
|
||||
verts[1].tangents[1][0] = 0;
|
||||
verts[1].tangents[1][1] = 1;
|
||||
verts[1].tangents[1][2] = 0;
|
||||
verts[2].xyz[0] = x + w;
|
||||
verts[2].xyz[1] = y + h;
|
||||
verts[2].xyz[2] = 0;
|
||||
verts[2].st[0] = s2;
|
||||
verts[2].st[1] = t2;
|
||||
verts[2].normal[0] = 0;
|
||||
verts[2].normal[1] = 0;
|
||||
verts[2].normal[2] = 1;
|
||||
verts[2].tangents[0][0] = 1;
|
||||
verts[2].tangents[0][1] = 0;
|
||||
verts[2].tangents[0][2] = 0;
|
||||
verts[2].tangents[1][0] = 0;
|
||||
verts[2].tangents[1][1] = 1;
|
||||
verts[2].tangents[1][2] = 0;
|
||||
verts[3].xyz[0] = x;
|
||||
verts[3].xyz[1] = y + h;
|
||||
verts[3].xyz[2] = 0;
|
||||
verts[3].st[0] = s1;
|
||||
verts[3].st[1] = t2;
|
||||
verts[3].normal[0] = 0;
|
||||
verts[3].normal[1] = 0;
|
||||
verts[3].normal[2] = 1;
|
||||
verts[3].tangents[0][0] = 1;
|
||||
verts[3].tangents[0][1] = 0;
|
||||
verts[3].tangents[0][2] = 0;
|
||||
verts[3].tangents[1][0] = 0;
|
||||
verts[3].tangents[1][1] = 1;
|
||||
verts[3].tangents[1][2] = 0;
|
||||
|
||||
DrawStretchPic( &verts[0], &indexes[0], 4, 6, hShader, false, 0.0f, 0.0f, 640.0f, 480.0f );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
DrawStretchTri
|
||||
|
||||
x/y/w/h are in the 0,0 to 640,480 range
|
||||
=============
|
||||
*/
|
||||
void idGuiModel::DrawStretchTri( idVec2 p1, idVec2 p2, idVec2 p3, idVec2 t1, idVec2 t2, idVec2 t3, const idMaterial *material ) {
|
||||
idDrawVert tempVerts[3];
|
||||
glIndex_t tempIndexes[3];
|
||||
int vertCount = 3;
|
||||
int indexCount = 3;
|
||||
|
||||
if ( !glConfig.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
if ( !material ) {
|
||||
return;
|
||||
}
|
||||
|
||||
tempIndexes[0] = 1;
|
||||
tempIndexes[1] = 0;
|
||||
tempIndexes[2] = 2;
|
||||
tempVerts[0].xyz[0] = p1.x;
|
||||
tempVerts[0].xyz[1] = p1.y;
|
||||
tempVerts[0].xyz[2] = 0;
|
||||
tempVerts[0].st[0] = t1.x;
|
||||
tempVerts[0].st[1] = t1.y;
|
||||
tempVerts[0].normal[0] = 0;
|
||||
tempVerts[0].normal[1] = 0;
|
||||
tempVerts[0].normal[2] = 1;
|
||||
tempVerts[0].tangents[0][0] = 1;
|
||||
tempVerts[0].tangents[0][1] = 0;
|
||||
tempVerts[0].tangents[0][2] = 0;
|
||||
tempVerts[0].tangents[1][0] = 0;
|
||||
tempVerts[0].tangents[1][1] = 1;
|
||||
tempVerts[0].tangents[1][2] = 0;
|
||||
tempVerts[1].xyz[0] = p2.x;
|
||||
tempVerts[1].xyz[1] = p2.y;
|
||||
tempVerts[1].xyz[2] = 0;
|
||||
tempVerts[1].st[0] = t2.x;
|
||||
tempVerts[1].st[1] = t2.y;
|
||||
tempVerts[1].normal[0] = 0;
|
||||
tempVerts[1].normal[1] = 0;
|
||||
tempVerts[1].normal[2] = 1;
|
||||
tempVerts[1].tangents[0][0] = 1;
|
||||
tempVerts[1].tangents[0][1] = 0;
|
||||
tempVerts[1].tangents[0][2] = 0;
|
||||
tempVerts[1].tangents[1][0] = 0;
|
||||
tempVerts[1].tangents[1][1] = 1;
|
||||
tempVerts[1].tangents[1][2] = 0;
|
||||
tempVerts[2].xyz[0] = p3.x;
|
||||
tempVerts[2].xyz[1] = p3.y;
|
||||
tempVerts[2].xyz[2] = 0;
|
||||
tempVerts[2].st[0] = t3.x;
|
||||
tempVerts[2].st[1] = t3.y;
|
||||
tempVerts[2].normal[0] = 0;
|
||||
tempVerts[2].normal[1] = 0;
|
||||
tempVerts[2].normal[2] = 1;
|
||||
tempVerts[2].tangents[0][0] = 1;
|
||||
tempVerts[2].tangents[0][1] = 0;
|
||||
tempVerts[2].tangents[0][2] = 0;
|
||||
tempVerts[2].tangents[1][0] = 0;
|
||||
tempVerts[2].tangents[1][1] = 1;
|
||||
tempVerts[2].tangents[1][2] = 0;
|
||||
|
||||
// break the current surface if we are changing to a new material
|
||||
if ( material != surf->material ) {
|
||||
if ( surf->numVerts ) {
|
||||
AdvanceSurf();
|
||||
}
|
||||
const_cast<idMaterial *>(material)->EnsureNotPurged(); // in case it was a gui item started before a level change
|
||||
surf->material = material;
|
||||
}
|
||||
|
||||
|
||||
int numVerts = verts.Num();
|
||||
int numIndexes = indexes.Num();
|
||||
|
||||
verts.AssureSize( numVerts + vertCount );
|
||||
indexes.AssureSize( numIndexes + indexCount );
|
||||
|
||||
surf->numVerts += vertCount;
|
||||
surf->numIndexes += indexCount;
|
||||
|
||||
for ( int i = 0; i < indexCount; i++ ) {
|
||||
indexes[numIndexes + i] = numVerts + tempIndexes[i] - surf->firstVert;
|
||||
}
|
||||
|
||||
memcpy( &verts[numVerts], tempVerts, vertCount * sizeof( verts[0] ) );
|
||||
}
|
||||
|
||||
70
neo/renderer/GuiModel.h
Normal file
70
neo/renderer/GuiModel.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
|
||||
typedef struct {
|
||||
const idMaterial *material;
|
||||
float color[4];
|
||||
int firstVert;
|
||||
int numVerts;
|
||||
int firstIndex;
|
||||
int numIndexes;
|
||||
} guiModelSurface_t;
|
||||
|
||||
class idGuiModel {
|
||||
public:
|
||||
idGuiModel();
|
||||
|
||||
void Clear();
|
||||
|
||||
void WriteToDemo( idDemoFile *demo );
|
||||
void ReadFromDemo( idDemoFile *demo );
|
||||
|
||||
void EmitToCurrentView( float modelMatrix[16], bool depthHack );
|
||||
void EmitFullScreen();
|
||||
|
||||
// these calls are forwarded from the renderer
|
||||
void SetColor( float r, float g, float b, float a );
|
||||
void DrawStretchPic( const idDrawVert *verts, const glIndex_t *indexes, int vertCount, int indexCount, const idMaterial *hShader,
|
||||
bool clip = true, float min_x = 0.0f, float min_y = 0.0f, float max_x = 640.0f, float max_y = 480.0f );
|
||||
void DrawStretchPic( float x, float y, float w, float h,
|
||||
float s1, float t1, float s2, float t2, const idMaterial *hShader);
|
||||
void DrawStretchTri ( idVec2 p1, idVec2 p2, idVec2 p3, idVec2 t1, idVec2 t2, idVec2 t3, const idMaterial *material );
|
||||
|
||||
//---------------------------
|
||||
private:
|
||||
void AdvanceSurf();
|
||||
void EmitSurface( guiModelSurface_t *surf, float modelMatrix[16], float modelViewMatrix[16], bool depthHack );
|
||||
|
||||
guiModelSurface_t *surf;
|
||||
|
||||
idList<guiModelSurface_t> surfaces;
|
||||
idList<glIndex_t> indexes;
|
||||
idList<idDrawVert> verts;
|
||||
};
|
||||
|
||||
496
neo/renderer/Image.h
Normal file
496
neo/renderer/Image.h
Normal file
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
====================================================================
|
||||
|
||||
IMAGE
|
||||
|
||||
idImage have a one to one correspondance with OpenGL textures.
|
||||
|
||||
No texture is ever used that does not have a corresponding idImage.
|
||||
|
||||
no code outside this unit should call any of these OpenGL functions:
|
||||
|
||||
qglGenTextures
|
||||
qglDeleteTextures
|
||||
qglBindTexture
|
||||
|
||||
qglTexParameter
|
||||
|
||||
qglTexImage
|
||||
qglTexSubImage
|
||||
|
||||
qglCopyTexImage
|
||||
qglCopyTexSubImage
|
||||
|
||||
qglEnable( GL_TEXTURE_* )
|
||||
qglDisable( GL_TEXTURE_* )
|
||||
|
||||
====================================================================
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
IS_UNLOADED, // no gl texture number
|
||||
IS_PARTIAL, // has a texture number and the low mip levels loaded
|
||||
IS_LOADED // has a texture number and the full mip hierarchy
|
||||
} imageState_t;
|
||||
|
||||
static const int MAX_TEXTURE_LEVELS = 14;
|
||||
|
||||
// surface description flags
|
||||
const unsigned long DDSF_CAPS = 0x00000001l;
|
||||
const unsigned long DDSF_HEIGHT = 0x00000002l;
|
||||
const unsigned long DDSF_WIDTH = 0x00000004l;
|
||||
const unsigned long DDSF_PITCH = 0x00000008l;
|
||||
const unsigned long DDSF_PIXELFORMAT = 0x00001000l;
|
||||
const unsigned long DDSF_MIPMAPCOUNT = 0x00020000l;
|
||||
const unsigned long DDSF_LINEARSIZE = 0x00080000l;
|
||||
const unsigned long DDSF_DEPTH = 0x00800000l;
|
||||
|
||||
// pixel format flags
|
||||
const unsigned long DDSF_ALPHAPIXELS = 0x00000001l;
|
||||
const unsigned long DDSF_FOURCC = 0x00000004l;
|
||||
const unsigned long DDSF_RGB = 0x00000040l;
|
||||
const unsigned long DDSF_RGBA = 0x00000041l;
|
||||
|
||||
// our extended flags
|
||||
const unsigned long DDSF_ID_INDEXCOLOR = 0x10000000l;
|
||||
const unsigned long DDSF_ID_MONOCHROME = 0x20000000l;
|
||||
|
||||
// dwCaps1 flags
|
||||
const unsigned long DDSF_COMPLEX = 0x00000008l;
|
||||
const unsigned long DDSF_TEXTURE = 0x00001000l;
|
||||
const unsigned long DDSF_MIPMAP = 0x00400000l;
|
||||
|
||||
#define DDS_MAKEFOURCC(a, b, c, d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
|
||||
|
||||
typedef struct {
|
||||
unsigned long dwSize;
|
||||
unsigned long dwFlags;
|
||||
unsigned long dwFourCC;
|
||||
unsigned long dwRGBBitCount;
|
||||
unsigned long dwRBitMask;
|
||||
unsigned long dwGBitMask;
|
||||
unsigned long dwBBitMask;
|
||||
unsigned long dwABitMask;
|
||||
} ddsFilePixelFormat_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long dwSize;
|
||||
unsigned long dwFlags;
|
||||
unsigned long dwHeight;
|
||||
unsigned long dwWidth;
|
||||
unsigned long dwPitchOrLinearSize;
|
||||
unsigned long dwDepth;
|
||||
unsigned long dwMipMapCount;
|
||||
unsigned long dwReserved1[11];
|
||||
ddsFilePixelFormat_t ddspf;
|
||||
unsigned long dwCaps1;
|
||||
unsigned long dwCaps2;
|
||||
unsigned long dwReserved2[3];
|
||||
} ddsFileHeader_t;
|
||||
|
||||
|
||||
// increasing numeric values imply more information is stored
|
||||
typedef enum {
|
||||
TD_SPECULAR, // may be compressed, and always zeros the alpha channel
|
||||
TD_DIFFUSE, // may be compressed
|
||||
TD_DEFAULT, // will use compressed formats when possible
|
||||
TD_BUMP, // may be compressed with 8 bit lookup
|
||||
TD_HIGH_QUALITY // either 32 bit or a component format, no loss at all
|
||||
} textureDepth_t;
|
||||
|
||||
typedef enum {
|
||||
TT_DISABLED,
|
||||
TT_2D,
|
||||
TT_3D,
|
||||
TT_CUBIC,
|
||||
TT_RECT
|
||||
} textureType_t;
|
||||
|
||||
typedef enum {
|
||||
CF_2D, // not a cube map
|
||||
CF_NATIVE, // _px, _nx, _py, etc, directly sent to GL
|
||||
CF_CAMERA // _forward, _back, etc, rotated and flipped as needed before sending to GL
|
||||
} cubeFiles_t;
|
||||
|
||||
#define MAX_IMAGE_NAME 256
|
||||
|
||||
class idImage {
|
||||
public:
|
||||
idImage();
|
||||
|
||||
// Makes this image active on the current GL texture unit.
|
||||
// automatically enables or disables cube mapping or texture3D
|
||||
// May perform file loading if the image was not preloaded.
|
||||
// May start a background image read.
|
||||
void Bind();
|
||||
|
||||
// for use with fragment programs, doesn't change any enable2D/3D/cube states
|
||||
void BindFragment();
|
||||
|
||||
// deletes the texture object, but leaves the structure so it can be reloaded
|
||||
void PurgeImage();
|
||||
|
||||
// used by callback functions to specify the actual data
|
||||
// data goes from the bottom to the top line of the image, as OpenGL expects it
|
||||
// These perform an implicit Bind() on the current texture unit
|
||||
// FIXME: should we implement cinematics this way, instead of with explicit calls?
|
||||
void GenerateImage( const byte *pic, int width, int height,
|
||||
textureFilter_t filter, bool allowDownSize,
|
||||
textureRepeat_t repeat, textureDepth_t depth );
|
||||
void Generate3DImage( const byte *pic, int width, int height, int depth,
|
||||
textureFilter_t filter, bool allowDownSize,
|
||||
textureRepeat_t repeat, textureDepth_t minDepth );
|
||||
void GenerateCubeImage( const byte *pic[6], int size,
|
||||
textureFilter_t filter, bool allowDownSize,
|
||||
textureDepth_t depth );
|
||||
|
||||
void CopyFramebuffer( int x, int y, int width, int height, bool useOversizedBuffer );
|
||||
|
||||
void CopyDepthbuffer( int x, int y, int width, int height );
|
||||
|
||||
void UploadScratch( const byte *pic, int width, int height );
|
||||
|
||||
// just for resource tracking
|
||||
void SetClassification( int tag );
|
||||
|
||||
// estimates size of the GL image based on dimensions and storage type
|
||||
int StorageSize() const;
|
||||
|
||||
// print a one line summary of the image
|
||||
void Print() const;
|
||||
|
||||
// check for changed timestamp on disk and reload if necessary
|
||||
void Reload( bool checkPrecompressed, bool force );
|
||||
|
||||
void AddReference() { refCount++; };
|
||||
|
||||
//==========================================================
|
||||
|
||||
void GetDownsize( int &scaled_width, int &scaled_height ) const;
|
||||
void MakeDefault(); // fill with a grid pattern
|
||||
void SetImageFilterAndRepeat() const;
|
||||
bool ShouldImageBePartialCached();
|
||||
void WritePrecompressedImage();
|
||||
bool CheckPrecompressedImage( bool fullLoad );
|
||||
void UploadPrecompressedImage( byte *data, int len );
|
||||
void ActuallyLoadImage( bool checkForPrecompressed, bool fromBackEnd );
|
||||
void StartBackgroundImageLoad();
|
||||
int BitsForInternalFormat( int internalFormat ) const;
|
||||
void UploadCompressedNormalMap( int width, int height, const byte *rgba, int mipLevel );
|
||||
GLenum SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, int width, int height,
|
||||
textureDepth_t minimumDepth, bool *monochromeResult ) const;
|
||||
void ImageProgramStringToCompressedFileName( const char *imageProg, char *fileName ) const;
|
||||
int NumLevelsForImageSize( int width, int height ) const;
|
||||
|
||||
// data commonly accessed is grouped here
|
||||
static const int TEXTURE_NOT_LOADED = -1;
|
||||
GLuint texnum; // gl texture binding, will be TEXTURE_NOT_LOADED if not loaded
|
||||
textureType_t type;
|
||||
int frameUsed; // for texture usage in frame statistics
|
||||
int bindCount; // incremented each bind
|
||||
|
||||
// background loading information
|
||||
idImage *partialImage; // shrunken, space-saving version
|
||||
bool isPartialImage; // true if this is pointed to by another image
|
||||
bool backgroundLoadInProgress; // true if another thread is reading the complete d3t file
|
||||
backgroundDownload_t bgl;
|
||||
idImage * bglNext; // linked from tr.backgroundImageLoads
|
||||
|
||||
// parameters that define this image
|
||||
idStr imgName; // game path, including extension (except for cube maps), may be an image program
|
||||
void (*generatorFunction)( idImage *image ); // NULL for files
|
||||
bool allowDownSize; // this also doubles as a don't-partially-load flag
|
||||
textureFilter_t filter;
|
||||
textureRepeat_t repeat;
|
||||
textureDepth_t depth;
|
||||
cubeFiles_t cubeFiles; // determines the naming and flipping conventions for the six images
|
||||
|
||||
bool referencedOutsideLevelLoad;
|
||||
bool levelLoadReferenced; // for determining if it needs to be purged
|
||||
bool precompressedFile; // true when it was loaded from a .d3t file
|
||||
bool defaulted; // true if the default image was generated because a file couldn't be loaded
|
||||
bool isMonochrome; // so the NV20 path can use a reduced pass count
|
||||
ID_TIME_T timestamp; // the most recent of all images used in creation, for reloadImages command
|
||||
|
||||
int imageHash; // for identical-image checking
|
||||
|
||||
int classification; // just for resource profiling
|
||||
|
||||
// data for listImages
|
||||
int uploadWidth, uploadHeight, uploadDepth; // after power of two, downsample, and MAX_TEXTURE_SIZE
|
||||
int internalFormat;
|
||||
|
||||
idImage *cacheUsagePrev, *cacheUsageNext; // for dynamic cache purging of old images
|
||||
|
||||
idImage * hashNext; // for hash chains to speed lookup
|
||||
|
||||
int refCount; // overall ref count
|
||||
};
|
||||
|
||||
ID_INLINE idImage::idImage() {
|
||||
texnum = TEXTURE_NOT_LOADED;
|
||||
partialImage = NULL;
|
||||
type = TT_DISABLED;
|
||||
isPartialImage = false;
|
||||
frameUsed = 0;
|
||||
classification = 0;
|
||||
backgroundLoadInProgress = false;
|
||||
bgl.opcode = DLTYPE_FILE;
|
||||
bgl.f = NULL;
|
||||
bglNext = NULL;
|
||||
imgName[0] = '\0';
|
||||
generatorFunction = NULL;
|
||||
allowDownSize = false;
|
||||
filter = TF_DEFAULT;
|
||||
repeat = TR_REPEAT;
|
||||
depth = TD_DEFAULT;
|
||||
cubeFiles = CF_2D;
|
||||
referencedOutsideLevelLoad = false;
|
||||
levelLoadReferenced = false;
|
||||
precompressedFile = false;
|
||||
defaulted = false;
|
||||
timestamp = 0;
|
||||
bindCount = 0;
|
||||
uploadWidth = uploadHeight = uploadDepth = 0;
|
||||
internalFormat = 0;
|
||||
cacheUsagePrev = cacheUsageNext = NULL;
|
||||
hashNext = NULL;
|
||||
isMonochrome = false;
|
||||
refCount = 0;
|
||||
}
|
||||
|
||||
|
||||
// data is RGBA
|
||||
void R_WriteTGA( const char *filename, const byte *data, int width, int height, bool flipVertical = false );
|
||||
// data is an 8 bit index into palette, which is RGB (no A)
|
||||
void R_WritePalTGA( const char *filename, const byte *data, const byte *palette, int width, int height, bool flipVertical = false );
|
||||
// data is in top-to-bottom raster order unless flipVertical is set
|
||||
|
||||
|
||||
class idImageManager {
|
||||
public:
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
// If the exact combination of parameters has been asked for already, an existing
|
||||
// image will be returned, otherwise a new image will be created.
|
||||
// Be careful not to use the same image file with different filter / repeat / etc parameters
|
||||
// if possible, because it will cause a second copy to be loaded.
|
||||
// If the load fails for any reason, the image will be filled in with the default
|
||||
// grid pattern.
|
||||
// Will automatically resample non-power-of-two images and execute image programs if needed.
|
||||
idImage * ImageFromFile( const char *name,
|
||||
textureFilter_t filter, bool allowDownSize,
|
||||
textureRepeat_t repeat, textureDepth_t depth, cubeFiles_t cubeMap = CF_2D );
|
||||
|
||||
// look for a loaded image, whatever the parameters
|
||||
idImage * GetImage( const char *name ) const;
|
||||
|
||||
// The callback will be issued immediately, and later if images are reloaded or vid_restart
|
||||
// The callback function should call one of the idImage::Generate* functions to fill in the data
|
||||
idImage * ImageFromFunction( const char *name, void (*generatorFunction)( idImage *image ));
|
||||
|
||||
// called once a frame to allow any background loads that have been completed
|
||||
// to turn into textures.
|
||||
void CompleteBackgroundImageLoads();
|
||||
|
||||
// returns the number of bytes of image data bound in the previous frame
|
||||
int SumOfUsedImages();
|
||||
|
||||
// called each frame to allow some cvars to automatically force changes
|
||||
void CheckCvars();
|
||||
|
||||
// purges all the images before a vid_restart
|
||||
void PurgeAllImages();
|
||||
|
||||
// reloads all apropriate images after a vid_restart
|
||||
void ReloadAllImages();
|
||||
|
||||
// disable the active texture unit
|
||||
void BindNull();
|
||||
|
||||
// Mark all file based images as currently unused,
|
||||
// but don't free anything. Calls to ImageFromFile() will
|
||||
// either mark the image as used, or create a new image without
|
||||
// loading the actual data.
|
||||
// Called only by renderSystem::BeginLevelLoad
|
||||
void BeginLevelLoad();
|
||||
|
||||
// Free all images marked as unused, and load all images that are necessary.
|
||||
// This architecture prevents us from having the union of two level's
|
||||
// worth of data present at one time.
|
||||
// Called only by renderSystem::EndLevelLoad
|
||||
void EndLevelLoad();
|
||||
|
||||
// used to clear and then write the dds conversion batch file
|
||||
void StartBuild();
|
||||
void FinishBuild( bool removeDups = false );
|
||||
void AddDDSCommand( const char *cmd );
|
||||
|
||||
void PrintMemInfo( MemInfo_t *mi );
|
||||
|
||||
// cvars
|
||||
static idCVar image_roundDown; // round bad sizes down to nearest power of two
|
||||
static idCVar image_colorMipLevels; // development aid to see texture mip usage
|
||||
static idCVar image_downSize; // controls texture downsampling
|
||||
static idCVar image_useCompression; // 0 = force everything to high quality
|
||||
static idCVar image_filter; // changes texture filtering on mipmapped images
|
||||
static idCVar image_anisotropy; // set the maximum texture anisotropy if available
|
||||
static idCVar image_lodbias; // change lod bias on mipmapped images
|
||||
static idCVar image_useAllFormats; // allow alpha/intensity/luminance/luminance+alpha
|
||||
static idCVar image_usePrecompressedTextures; // use .dds files if present
|
||||
static idCVar image_writePrecompressedTextures; // write .dds files if necessary
|
||||
static idCVar image_writeNormalTGA; // debug tool to write out .tgas of the final normal maps
|
||||
static idCVar image_writeNormalTGAPalletized; // debug tool to write out palletized versions of the final normal maps
|
||||
static idCVar image_writeTGA; // debug tool to write out .tgas of the non normal maps
|
||||
static idCVar image_useNormalCompression; // 1 = use 256 color compression for normal maps if available, 2 = use rxgb compression
|
||||
static idCVar image_useOffLineCompression; // will write a batch file with commands for the offline compression
|
||||
static idCVar image_preload; // if 0, dynamically load all images
|
||||
static idCVar image_cacheMinK; // maximum K of precompressed files to read at specification time,
|
||||
// the remainder will be dynamically cached
|
||||
static idCVar image_cacheMegs; // maximum bytes set aside for temporary loading of full-sized precompressed images
|
||||
static idCVar image_useCache; // 1 = do background load image caching
|
||||
static idCVar image_showBackgroundLoads; // 1 = print number of outstanding background loads
|
||||
static idCVar image_forceDownSize; // allows the ability to force a downsize
|
||||
static idCVar image_downSizeSpecular; // downsize specular
|
||||
static idCVar image_downSizeSpecularLimit;// downsize specular limit
|
||||
static idCVar image_downSizeBump; // downsize bump maps
|
||||
static idCVar image_downSizeBumpLimit; // downsize bump limit
|
||||
static idCVar image_ignoreHighQuality; // ignore high quality on materials
|
||||
static idCVar image_downSizeLimit; // downsize diffuse limit
|
||||
|
||||
// built-in images
|
||||
idImage * defaultImage;
|
||||
idImage * flatNormalMap; // 128 128 255 in all pixels
|
||||
idImage * ambientNormalMap; // tr.ambientLightVector encoded in all pixels
|
||||
idImage * rampImage; // 0-255 in RGBA in S
|
||||
idImage * alphaRampImage; // 0-255 in alpha, 255 in RGB
|
||||
idImage * alphaNotchImage; // 2x1 texture with just 1110 and 1111 with point sampling
|
||||
idImage * whiteImage; // full of 0xff
|
||||
idImage * blackImage; // full of 0x00
|
||||
idImage * normalCubeMapImage; // cube map to normalize STR into RGB
|
||||
idImage * noFalloffImage; // all 255, but zero clamped
|
||||
idImage * fogImage; // increasing alpha is denser fog
|
||||
idImage * fogEnterImage; // adjust fogImage alpha based on terminator plane
|
||||
idImage * cinematicImage;
|
||||
idImage * scratchImage;
|
||||
idImage * scratchImage2;
|
||||
idImage * accumImage;
|
||||
idImage * currentRenderImage; // for SS_POST_PROCESS shaders
|
||||
idImage * scratchCubeMapImage;
|
||||
idImage * specularTableImage; // 1D intensity texture with our specular function
|
||||
idImage * specular2DTableImage; // 2D intensity texture with our specular function with variable specularity
|
||||
idImage * borderClampImage; // white inside, black outside
|
||||
|
||||
//--------------------------------------------------------
|
||||
|
||||
idImage * AllocImage( const char *name );
|
||||
void SetNormalPalette();
|
||||
void ChangeTextureFilter();
|
||||
|
||||
idList<idImage*> images;
|
||||
idStrList ddsList;
|
||||
idHashIndex ddsHash;
|
||||
|
||||
bool insideLevelLoad; // don't actually load images now
|
||||
|
||||
byte originalToCompressed[256]; // maps normal maps to 8 bit textures
|
||||
byte compressedPalette[768]; // the palette that normal maps use
|
||||
|
||||
// default filter modes for images
|
||||
GLenum textureMinFilter;
|
||||
GLenum textureMaxFilter;
|
||||
float textureAnisotropy;
|
||||
float textureLODBias;
|
||||
|
||||
idImage * imageHashTable[FILE_HASH_SIZE];
|
||||
|
||||
idImage * backgroundImageLoads; // chain of images that have background file loads active
|
||||
idImage cacheLRU; // head/tail of doubly linked list
|
||||
int totalCachedImageSize; // for determining when something should be purged
|
||||
|
||||
int numActiveBackgroundImageLoads;
|
||||
const static int MAX_BACKGROUND_IMAGE_LOADS = 8;
|
||||
};
|
||||
|
||||
extern idImageManager *globalImages; // pointer to global list for the rest of the system
|
||||
|
||||
int MakePowerOfTwo( int num );
|
||||
|
||||
/*
|
||||
====================================================================
|
||||
|
||||
IMAGEPROCESS
|
||||
|
||||
FIXME: make an "imageBlock" type to hold byte*,width,height?
|
||||
====================================================================
|
||||
*/
|
||||
|
||||
byte *R_Dropsample( const byte *in, int inwidth, int inheight,
|
||||
int outwidth, int outheight );
|
||||
byte *R_ResampleTexture( const byte *in, int inwidth, int inheight,
|
||||
int outwidth, int outheight );
|
||||
byte *R_MipMapWithAlphaSpecularity( const byte *in, int width, int height );
|
||||
byte *R_MipMap( const byte *in, int width, int height, bool preserveBorder );
|
||||
byte *R_MipMap3D( const byte *in, int width, int height, int depth, bool preserveBorder );
|
||||
|
||||
// these operate in-place on the provided pixels
|
||||
void R_SetBorderTexels( byte *inBase, int width, int height, const byte border[4] );
|
||||
void R_SetBorderTexels3D( byte *inBase, int width, int height, int depth, const byte border[4] );
|
||||
void R_BlendOverTexture( byte *data, int pixelCount, const byte blend[4] );
|
||||
void R_HorizontalFlip( byte *data, int width, int height );
|
||||
void R_VerticalFlip( byte *data, int width, int height );
|
||||
void R_RotatePic( byte *data, int width );
|
||||
|
||||
/*
|
||||
====================================================================
|
||||
|
||||
IMAGEFILES
|
||||
|
||||
====================================================================
|
||||
*/
|
||||
|
||||
void R_LoadImage( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp, bool makePowerOf2 );
|
||||
// pic is in top to bottom raster format
|
||||
bool R_LoadCubeImages( const char *cname, cubeFiles_t extensions, byte *pic[6], int *size, ID_TIME_T *timestamp );
|
||||
|
||||
/*
|
||||
====================================================================
|
||||
|
||||
IMAGEPROGRAM
|
||||
|
||||
====================================================================
|
||||
*/
|
||||
|
||||
void R_LoadImageProgram( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp, textureDepth_t *depth = NULL );
|
||||
const char *R_ParsePastImageProgram( idLexer &src );
|
||||
|
||||
1181
neo/renderer/Image_files.cpp
Normal file
1181
neo/renderer/Image_files.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2219
neo/renderer/Image_init.cpp
Normal file
2219
neo/renderer/Image_init.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2212
neo/renderer/Image_load.cpp
Normal file
2212
neo/renderer/Image_load.cpp
Normal file
File diff suppressed because it is too large
Load Diff
621
neo/renderer/Image_process.cpp
Normal file
621
neo/renderer/Image_process.cpp
Normal file
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
/*
|
||||
================
|
||||
R_ResampleTexture
|
||||
|
||||
Used to resample images in a more general than quartering fashion.
|
||||
|
||||
This will only have filter coverage if the resampled size
|
||||
is greater than half the original size.
|
||||
|
||||
If a larger shrinking is needed, use the mipmap function
|
||||
after resampling to the next lower power of two.
|
||||
================
|
||||
*/
|
||||
#define MAX_DIMENSION 4096
|
||||
byte *R_ResampleTexture( const byte *in, int inwidth, int inheight,
|
||||
int outwidth, int outheight ) {
|
||||
int i, j;
|
||||
const byte *inrow, *inrow2;
|
||||
unsigned int frac, fracstep;
|
||||
unsigned int p1[MAX_DIMENSION], p2[MAX_DIMENSION];
|
||||
const byte *pix1, *pix2, *pix3, *pix4;
|
||||
byte *out, *out_p;
|
||||
|
||||
if ( outwidth > MAX_DIMENSION ) {
|
||||
outwidth = MAX_DIMENSION;
|
||||
}
|
||||
if ( outheight > MAX_DIMENSION ) {
|
||||
outheight = MAX_DIMENSION;
|
||||
}
|
||||
|
||||
out = (byte *)R_StaticAlloc( outwidth * outheight * 4 );
|
||||
out_p = out;
|
||||
|
||||
fracstep = inwidth*0x10000/outwidth;
|
||||
|
||||
frac = fracstep>>2;
|
||||
for ( i=0 ; i<outwidth ; i++ ) {
|
||||
p1[i] = 4*(frac>>16);
|
||||
frac += fracstep;
|
||||
}
|
||||
frac = 3*(fracstep>>2);
|
||||
for ( i=0 ; i<outwidth ; i++ ) {
|
||||
p2[i] = 4*(frac>>16);
|
||||
frac += fracstep;
|
||||
}
|
||||
|
||||
for (i=0 ; i<outheight ; i++, out_p += outwidth*4 ) {
|
||||
inrow = in + 4 * inwidth * (int)( ( i + 0.25f ) * inheight / outheight );
|
||||
inrow2 = in + 4 * inwidth * (int)( ( i + 0.75f ) * inheight / outheight );
|
||||
frac = fracstep >> 1;
|
||||
for (j=0 ; j<outwidth ; j++) {
|
||||
pix1 = inrow + p1[j];
|
||||
pix2 = inrow + p2[j];
|
||||
pix3 = inrow2 + p1[j];
|
||||
pix4 = inrow2 + p2[j];
|
||||
out_p[j*4+0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2;
|
||||
out_p[j*4+1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2;
|
||||
out_p[j*4+2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2;
|
||||
out_p[j*4+3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
R_Dropsample
|
||||
|
||||
Used to resample images in a more general than quartering fashion.
|
||||
Normal maps and such should not be bilerped.
|
||||
================
|
||||
*/
|
||||
byte *R_Dropsample( const byte *in, int inwidth, int inheight,
|
||||
int outwidth, int outheight ) {
|
||||
int i, j, k;
|
||||
const byte *inrow;
|
||||
const byte *pix1;
|
||||
byte *out, *out_p;
|
||||
|
||||
out = (byte *)R_StaticAlloc( outwidth * outheight * 4 );
|
||||
out_p = out;
|
||||
|
||||
for (i=0 ; i<outheight ; i++, out_p += outwidth*4 ) {
|
||||
inrow = in + 4*inwidth*(int)((i+0.25)*inheight/outheight);
|
||||
for (j=0 ; j<outwidth ; j++) {
|
||||
k = j * inwidth / outwidth;
|
||||
pix1 = inrow + k * 4;
|
||||
out_p[j*4+0] = pix1[0];
|
||||
out_p[j*4+1] = pix1[1];
|
||||
out_p[j*4+2] = pix1[2];
|
||||
out_p[j*4+3] = pix1[3];
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
R_SetBorderTexels
|
||||
|
||||
===============
|
||||
*/
|
||||
void R_SetBorderTexels( byte *inBase, int width, int height, const byte border[4] ) {
|
||||
int i;
|
||||
byte *out;
|
||||
|
||||
out = inBase;
|
||||
for (i=0 ; i<height ; i++, out+=width*4) {
|
||||
out[0] = border[0];
|
||||
out[1] = border[1];
|
||||
out[2] = border[2];
|
||||
out[3] = border[3];
|
||||
}
|
||||
out = inBase+(width-1)*4;
|
||||
for (i=0 ; i<height ; i++, out+=width*4) {
|
||||
out[0] = border[0];
|
||||
out[1] = border[1];
|
||||
out[2] = border[2];
|
||||
out[3] = border[3];
|
||||
}
|
||||
out = inBase;
|
||||
for (i=0 ; i<width ; i++, out+=4) {
|
||||
out[0] = border[0];
|
||||
out[1] = border[1];
|
||||
out[2] = border[2];
|
||||
out[3] = border[3];
|
||||
}
|
||||
out = inBase+width*4*(height-1);
|
||||
for (i=0 ; i<width ; i++, out+=4) {
|
||||
out[0] = border[0];
|
||||
out[1] = border[1];
|
||||
out[2] = border[2];
|
||||
out[3] = border[3];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_SetBorderTexels3D
|
||||
|
||||
===============
|
||||
*/
|
||||
void R_SetBorderTexels3D( byte *inBase, int width, int height, int depth, const byte border[4] ) {
|
||||
int i, j;
|
||||
byte *out;
|
||||
int row, plane;
|
||||
|
||||
row = width * 4;
|
||||
plane = row * depth;
|
||||
|
||||
for ( j = 1 ; j < depth - 1 ; j++ ) {
|
||||
out = inBase + j * plane;
|
||||
for (i=0 ; i<height ; i++, out+=row) {
|
||||
out[0] = border[0];
|
||||
out[1] = border[1];
|
||||
out[2] = border[2];
|
||||
out[3] = border[3];
|
||||
}
|
||||
out = inBase+(width-1)*4 + j * plane;
|
||||
for (i=0 ; i<height ; i++, out+=row) {
|
||||
out[0] = border[0];
|
||||
out[1] = border[1];
|
||||
out[2] = border[2];
|
||||
out[3] = border[3];
|
||||
}
|
||||
out = inBase + j * plane;
|
||||
for (i=0 ; i<width ; i++, out+=4) {
|
||||
out[0] = border[0];
|
||||
out[1] = border[1];
|
||||
out[2] = border[2];
|
||||
out[3] = border[3];
|
||||
}
|
||||
out = inBase+width*4*(height-1) + j * plane;
|
||||
for (i=0 ; i<width ; i++, out+=4) {
|
||||
out[0] = border[0];
|
||||
out[1] = border[1];
|
||||
out[2] = border[2];
|
||||
out[3] = border[3];
|
||||
}
|
||||
}
|
||||
|
||||
out = inBase;
|
||||
for ( i = 0 ; i < plane ; i += 4, out += 4 ) {
|
||||
out[0] = border[0];
|
||||
out[1] = border[1];
|
||||
out[2] = border[2];
|
||||
out[3] = border[3];
|
||||
}
|
||||
out = inBase+(depth-1)*plane;
|
||||
for ( i = 0 ; i < plane ; i += 4, out += 4 ) {
|
||||
out[0] = border[0];
|
||||
out[1] = border[1];
|
||||
out[2] = border[2];
|
||||
out[3] = border[3];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
R_SetAlphaNormalDivergence
|
||||
|
||||
If any of the angles inside the cone would directly reflect to the light, there will be
|
||||
a specular highlight. The intensity of the highlight is inversely proportional to the
|
||||
area of the spread.
|
||||
|
||||
Light source area is important for the base size.
|
||||
|
||||
area subtended in light is the divergence times the distance
|
||||
|
||||
Shininess value is subtracted from the divergence
|
||||
|
||||
Sets the alpha channel to the greatest divergence dot product of the surrounding texels.
|
||||
1.0 = flat, 0.0 = turns a 90 degree angle
|
||||
Lower values give less shiny specular
|
||||
With mip maps, the lowest samnpled value will be retained
|
||||
|
||||
Should we rewrite the normal as the centered average?
|
||||
================
|
||||
*/
|
||||
void R_SetAlphaNormalDivergence( byte *in, int width, int height ) {
|
||||
for ( int y = 0 ; y < height ; y++ ) {
|
||||
for ( int x = 0 ; x < width ; x++ ) {
|
||||
// the divergence is the smallest dot product of any of the eight surrounding texels
|
||||
byte *pic_p = in + ( y * width + x ) * 4;
|
||||
idVec3 center;
|
||||
center[0] = ( pic_p[0] - 128 ) / 127;
|
||||
center[1] = ( pic_p[1] - 128 ) / 127;
|
||||
center[2] = ( pic_p[2] - 128 ) / 127;
|
||||
center.Normalize();
|
||||
|
||||
float maxDiverge = 1.0;
|
||||
|
||||
// FIXME: this assumes wrap mode, but should handle clamp modes and border colors
|
||||
for ( int yy = -1 ; yy <= 1 ; yy++ ) {
|
||||
for ( int xx = -1 ; xx <= 1 ; xx++ ) {
|
||||
if ( yy == 0 && xx == 0 ) {
|
||||
continue;
|
||||
}
|
||||
byte *corner_p = in + ( ((y+yy)&(height-1)) * width + ((x+xx)&width-1) ) * 4;
|
||||
idVec3 corner;
|
||||
corner[0] = ( corner_p[0] - 128 ) / 127;
|
||||
corner[1] = ( corner_p[1] - 128 ) / 127;
|
||||
corner[2] = ( corner_p[2] - 128 ) / 127;
|
||||
corner.Normalize();
|
||||
|
||||
float diverge = corner * center;
|
||||
if ( diverge < maxDiverge ) {
|
||||
maxDiverge = diverge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we can get a diverge < 0 in some extreme cases
|
||||
if ( maxDiverge < 0 ) {
|
||||
maxDiverge = 0;
|
||||
}
|
||||
pic_p[3] = maxDiverge * 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
R_MipMapWithAlphaSpecularity
|
||||
|
||||
Returns a new copy of the texture, quartered in size and filtered.
|
||||
The alpha channel is taken to be the minimum of the dots of all surrounding normals.
|
||||
================
|
||||
*/
|
||||
#define MIP_MIN(a,b) (a<b?a:b)
|
||||
|
||||
byte *R_MipMapWithAlphaSpecularity( const byte *in, int width, int height ) {
|
||||
int i, j, c, x, y, sx, sy;
|
||||
const byte *in_p;
|
||||
byte *out, *out_p;
|
||||
int row;
|
||||
int newWidth, newHeight;
|
||||
float *fbuf, *fbuf_p;
|
||||
|
||||
if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
|
||||
common->FatalError( "R_MipMapWithAlphaMin called with size %i,%i", width, height );
|
||||
}
|
||||
|
||||
// convert the incoming texture to centered floating point
|
||||
c = width * height;
|
||||
fbuf = (float *)_alloca( c * 4 * sizeof( *fbuf ) );
|
||||
in_p = in;
|
||||
fbuf_p = fbuf;
|
||||
for ( i = 0 ; i < c ; i++, in_p+=4, fbuf_p += 4 ) {
|
||||
fbuf_p[0] = ( in_p[0] / 255.0 ) * 2.0 - 1.0; // convert to a normal
|
||||
fbuf_p[1] = ( in_p[1] / 255.0 ) * 2.0 - 1.0;
|
||||
fbuf_p[2] = ( in_p[2] / 255.0 ) * 2.0 - 1.0;
|
||||
fbuf_p[3] = ( in_p[3] / 255.0 ); // filtered divegence / specularity
|
||||
}
|
||||
|
||||
row = width * 4;
|
||||
|
||||
newWidth = width >> 1;
|
||||
newHeight = height >> 1;
|
||||
if ( !newWidth ) {
|
||||
newWidth = 1;
|
||||
}
|
||||
if ( !newHeight ) {
|
||||
newHeight = 1;
|
||||
}
|
||||
out = (byte *)R_StaticAlloc( newWidth * newHeight * 4 );
|
||||
out_p = out;
|
||||
|
||||
in_p = in;
|
||||
|
||||
for ( i=0 ; i<newHeight ; i++ ) {
|
||||
for ( j=0 ; j<newWidth ; j++, out_p+=4 ) {
|
||||
idVec3 total;
|
||||
float totalSpec;
|
||||
|
||||
total.Zero();
|
||||
totalSpec = 0;
|
||||
// find the average normal
|
||||
for ( x = -1 ; x <= 1 ; x++ ) {
|
||||
sx = ( j * 2 + x ) & (width-1);
|
||||
for ( y = -1 ; y <= 1 ; y++ ) {
|
||||
sy = ( i * 2 + y ) & (height-1);
|
||||
fbuf_p = fbuf + ( sy * width + sx ) * 4;
|
||||
|
||||
total[0] += fbuf_p[0];
|
||||
total[1] += fbuf_p[1];
|
||||
total[2] += fbuf_p[2];
|
||||
|
||||
totalSpec += fbuf_p[3];
|
||||
}
|
||||
}
|
||||
total.Normalize();
|
||||
totalSpec /= 9.0;
|
||||
|
||||
// find the maximum divergence
|
||||
for ( x = -1 ; x <= 1 ; x++ ) {
|
||||
for ( y = -1 ; y <= 1 ; y++ ) {
|
||||
}
|
||||
}
|
||||
|
||||
// store the average normal and divergence
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
R_MipMap
|
||||
|
||||
Returns a new copy of the texture, quartered in size and filtered.
|
||||
|
||||
If a texture is intended to be used in GL_CLAMP or GL_CLAMP_TO_EDGE mode with
|
||||
a completely transparent border, we must prevent any blurring into the outer
|
||||
ring of texels by filling it with the border from the previous level. This
|
||||
will result in a slight shrinking of the texture as it mips, but better than
|
||||
smeared clamps...
|
||||
================
|
||||
*/
|
||||
byte *R_MipMap( const byte *in, int width, int height, bool preserveBorder ) {
|
||||
int i, j;
|
||||
const byte *in_p;
|
||||
byte *out, *out_p;
|
||||
int row;
|
||||
byte border[4];
|
||||
int newWidth, newHeight;
|
||||
|
||||
if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
|
||||
common->FatalError( "R_MipMap called with size %i,%i", width, height );
|
||||
}
|
||||
|
||||
border[0] = in[0];
|
||||
border[1] = in[1];
|
||||
border[2] = in[2];
|
||||
border[3] = in[3];
|
||||
|
||||
row = width * 4;
|
||||
|
||||
newWidth = width >> 1;
|
||||
newHeight = height >> 1;
|
||||
if ( !newWidth ) {
|
||||
newWidth = 1;
|
||||
}
|
||||
if ( !newHeight ) {
|
||||
newHeight = 1;
|
||||
}
|
||||
out = (byte *)R_StaticAlloc( newWidth * newHeight * 4 );
|
||||
out_p = out;
|
||||
|
||||
in_p = in;
|
||||
|
||||
width >>= 1;
|
||||
height >>= 1;
|
||||
|
||||
if ( width == 0 || height == 0 ) {
|
||||
width += height; // get largest
|
||||
if ( preserveBorder ) {
|
||||
for (i=0 ; i<width ; i++, out_p+=4 ) {
|
||||
out_p[0] = border[0];
|
||||
out_p[1] = border[1];
|
||||
out_p[2] = border[2];
|
||||
out_p[3] = border[3];
|
||||
}
|
||||
} else {
|
||||
for (i=0 ; i<width ; i++, out_p+=4, in_p+=8 ) {
|
||||
out_p[0] = ( in_p[0] + in_p[4] )>>1;
|
||||
out_p[1] = ( in_p[1] + in_p[5] )>>1;
|
||||
out_p[2] = ( in_p[2] + in_p[6] )>>1;
|
||||
out_p[3] = ( in_p[3] + in_p[7] )>>1;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
for (i=0 ; i<height ; i++, in_p+=row) {
|
||||
for (j=0 ; j<width ; j++, out_p+=4, in_p+=8) {
|
||||
out_p[0] = (in_p[0] + in_p[4] + in_p[row+0] + in_p[row+4])>>2;
|
||||
out_p[1] = (in_p[1] + in_p[5] + in_p[row+1] + in_p[row+5])>>2;
|
||||
out_p[2] = (in_p[2] + in_p[6] + in_p[row+2] + in_p[row+6])>>2;
|
||||
out_p[3] = (in_p[3] + in_p[7] + in_p[row+3] + in_p[row+7])>>2;
|
||||
}
|
||||
}
|
||||
|
||||
// copy the old border texel back around if desired
|
||||
if ( preserveBorder ) {
|
||||
R_SetBorderTexels( out, width, height, border );
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
R_MipMap3D
|
||||
|
||||
Returns a new copy of the texture, eigthed in size and filtered.
|
||||
|
||||
If a texture is intended to be used in GL_CLAMP or GL_CLAMP_TO_EDGE mode with
|
||||
a completely transparent border, we must prevent any blurring into the outer
|
||||
ring of texels by filling it with the border from the previous level. This
|
||||
will result in a slight shrinking of the texture as it mips, but better than
|
||||
smeared clamps...
|
||||
================
|
||||
*/
|
||||
byte *R_MipMap3D( const byte *in, int width, int height, int depth, bool preserveBorder ) {
|
||||
int i, j, k;
|
||||
const byte *in_p;
|
||||
byte *out, *out_p;
|
||||
int row, plane;
|
||||
byte border[4];
|
||||
int newWidth, newHeight, newDepth;
|
||||
|
||||
if ( depth == 1 ) {
|
||||
return R_MipMap( in, width, height, preserveBorder );
|
||||
}
|
||||
|
||||
// assume symetric for now
|
||||
if ( width < 2 || height < 2 || depth < 2 ) {
|
||||
common->FatalError( "R_MipMap3D called with size %i,%i,%i", width, height, depth );
|
||||
}
|
||||
|
||||
border[0] = in[0];
|
||||
border[1] = in[1];
|
||||
border[2] = in[2];
|
||||
border[3] = in[3];
|
||||
|
||||
row = width * 4;
|
||||
plane = row * height;
|
||||
|
||||
newWidth = width >> 1;
|
||||
newHeight = height >> 1;
|
||||
newDepth = depth >> 1;
|
||||
|
||||
out = (byte *)R_StaticAlloc( newWidth * newHeight * newDepth * 4 );
|
||||
out_p = out;
|
||||
|
||||
in_p = in;
|
||||
|
||||
width >>= 1;
|
||||
height >>= 1;
|
||||
depth >>= 1;
|
||||
|
||||
for (k=0 ; k<depth ; k++, in_p+=plane) {
|
||||
for (i=0 ; i<height ; i++, in_p+=row) {
|
||||
for (j=0 ; j<width ; j++, out_p+=4, in_p+=8) {
|
||||
out_p[0] = (in_p[0] + in_p[4] + in_p[row+0] + in_p[row+4] +
|
||||
in_p[plane+0] + in_p[plane+4] + in_p[plane+row+0] + in_p[plane+row+4]
|
||||
)>>3;
|
||||
out_p[1] = (in_p[1] + in_p[5] + in_p[row+1] + in_p[row+5] +
|
||||
in_p[plane+1] + in_p[plane+5] + in_p[plane+row+1] + in_p[plane+row+5]
|
||||
)>>3;
|
||||
out_p[2] = (in_p[2] + in_p[6] + in_p[row+2] + in_p[row+6] +
|
||||
in_p[plane+2] + in_p[plane+6] + in_p[plane+row+2] + in_p[plane+row+6]
|
||||
)>>3;
|
||||
out_p[3] = (in_p[3] + in_p[7] + in_p[row+3] + in_p[row+7] +
|
||||
in_p[plane+3] + in_p[plane+6] + in_p[plane+row+3] + in_p[plane+row+6]
|
||||
)>>3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy the old border texel back around if desired
|
||||
if ( preserveBorder ) {
|
||||
R_SetBorderTexels3D( out, width, height, depth, border );
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
R_BlendOverTexture
|
||||
|
||||
Apply a color blend over a set of pixels
|
||||
==================
|
||||
*/
|
||||
void R_BlendOverTexture( byte *data, int pixelCount, const byte blend[4] ) {
|
||||
int i;
|
||||
int inverseAlpha;
|
||||
int premult[3];
|
||||
|
||||
inverseAlpha = 255 - blend[3];
|
||||
premult[0] = blend[0] * blend[3];
|
||||
premult[1] = blend[1] * blend[3];
|
||||
premult[2] = blend[2] * blend[3];
|
||||
|
||||
for ( i = 0 ; i < pixelCount ; i++, data+=4 ) {
|
||||
data[0] = ( data[0] * inverseAlpha + premult[0] ) >> 9;
|
||||
data[1] = ( data[1] * inverseAlpha + premult[1] ) >> 9;
|
||||
data[2] = ( data[2] * inverseAlpha + premult[2] ) >> 9;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
R_HorizontalFlip
|
||||
|
||||
Flip the image in place
|
||||
==================
|
||||
*/
|
||||
void R_HorizontalFlip( byte *data, int width, int height ) {
|
||||
int i, j;
|
||||
int temp;
|
||||
|
||||
for ( i = 0 ; i < height ; i++ ) {
|
||||
for ( j = 0 ; j < width / 2 ; j++ ) {
|
||||
temp = *( (int *)data + i * width + j );
|
||||
*( (int *)data + i * width + j ) = *( (int *)data + i * width + width - 1 - j );
|
||||
*( (int *)data + i * width + width - 1 - j ) = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void R_VerticalFlip( byte *data, int width, int height ) {
|
||||
int i, j;
|
||||
int temp;
|
||||
|
||||
for ( i = 0 ; i < width ; i++ ) {
|
||||
for ( j = 0 ; j < height / 2 ; j++ ) {
|
||||
temp = *( (int *)data + j * width + i );
|
||||
*( (int *)data + j * width + i ) = *( (int *)data + ( height - 1 - j ) * width + i );
|
||||
*( (int *)data + ( height - 1 - j ) * width + i ) = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void R_RotatePic( byte *data, int width ) {
|
||||
int i, j;
|
||||
int *temp;
|
||||
|
||||
temp = (int *)R_StaticAlloc( width * width * 4 );
|
||||
|
||||
for ( i = 0 ; i < width ; i++ ) {
|
||||
for ( j = 0 ; j < width ; j++ ) {
|
||||
*( temp + i * width + j ) = *( (int *)data + j * width + i );
|
||||
}
|
||||
}
|
||||
|
||||
memcpy( data, temp, width * width * 4 );
|
||||
|
||||
R_StaticFree( temp );
|
||||
}
|
||||
|
||||
644
neo/renderer/Image_program.cpp
Normal file
644
neo/renderer/Image_program.cpp
Normal file
@@ -0,0 +1,644 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
all uncompressed
|
||||
uncompressed normal maps
|
||||
|
||||
downsample images
|
||||
|
||||
16 meg Dynamic cache
|
||||
|
||||
Anisotropic texturing
|
||||
|
||||
Trilinear on all
|
||||
Trilinear on normal maps, bilinear on others
|
||||
Bilinear on all
|
||||
|
||||
|
||||
Manager
|
||||
|
||||
->List
|
||||
->Print
|
||||
->Reload( bool force )
|
||||
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
// tr_imageprogram.c
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
/*
|
||||
|
||||
Anywhere that an image name is used (diffusemaps, bumpmaps, specularmaps, lights, etc),
|
||||
an imageProgram can be specified.
|
||||
|
||||
This allows load time operations, like heightmap-to-normalmap conversion and image
|
||||
composition, to be automatically handled in a way that supports timestamped reloads.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
=================
|
||||
R_HeightmapToNormalMap
|
||||
|
||||
it is not possible to convert a heightmap into a normal map
|
||||
properly without knowing the texture coordinate stretching.
|
||||
We can assume constant and equal ST vectors for walls, but not for characters.
|
||||
=================
|
||||
*/
|
||||
static void R_HeightmapToNormalMap( byte *data, int width, int height, float scale ) {
|
||||
int i, j;
|
||||
byte *depth;
|
||||
|
||||
scale = scale / 256;
|
||||
|
||||
// copy and convert to grey scale
|
||||
j = width * height;
|
||||
depth = (byte *)R_StaticAlloc( j );
|
||||
for ( i = 0 ; i < j ; i++ ) {
|
||||
depth[i] = ( data[i*4] + data[i*4+1] + data[i*4+2] ) / 3;
|
||||
}
|
||||
|
||||
idVec3 dir, dir2;
|
||||
for ( i = 0 ; i < height ; i++ ) {
|
||||
for ( j = 0 ; j < width ; j++ ) {
|
||||
int d1, d2, d3, d4;
|
||||
int a1, a2, a3, a4;
|
||||
|
||||
// FIXME: look at five points?
|
||||
|
||||
// look at three points to estimate the gradient
|
||||
a1 = d1 = depth[ ( i * width + j ) ];
|
||||
a2 = d2 = depth[ ( i * width + ( ( j + 1 ) & ( width - 1 ) ) ) ];
|
||||
a3 = d3 = depth[ ( ( ( i + 1 ) & ( height - 1 ) ) * width + j ) ];
|
||||
a4 = d4 = depth[ ( ( ( i + 1 ) & ( height - 1 ) ) * width + ( ( j + 1 ) & ( width - 1 ) ) ) ];
|
||||
|
||||
d2 -= d1;
|
||||
d3 -= d1;
|
||||
|
||||
dir[0] = -d2 * scale;
|
||||
dir[1] = -d3 * scale;
|
||||
dir[2] = 1;
|
||||
dir.NormalizeFast();
|
||||
|
||||
a1 -= a3;
|
||||
a4 -= a3;
|
||||
|
||||
dir2[0] = -a4 * scale;
|
||||
dir2[1] = a1 * scale;
|
||||
dir2[2] = 1;
|
||||
dir2.NormalizeFast();
|
||||
|
||||
dir += dir2;
|
||||
dir.NormalizeFast();
|
||||
|
||||
a1 = ( i * width + j ) * 4;
|
||||
data[ a1 + 0 ] = (byte)(dir[0] * 127 + 128);
|
||||
data[ a1 + 1 ] = (byte)(dir[1] * 127 + 128);
|
||||
data[ a1 + 2 ] = (byte)(dir[2] * 127 + 128);
|
||||
data[ a1 + 3 ] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
R_StaticFree( depth );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
R_ImageScale
|
||||
=================
|
||||
*/
|
||||
static void R_ImageScale( byte *data, int width, int height, float scale[4] ) {
|
||||
int i, j;
|
||||
int c;
|
||||
|
||||
c = width * height * 4;
|
||||
|
||||
for ( i = 0 ; i < c ; i++ ) {
|
||||
j = (byte)(data[i] * scale[i&3]);
|
||||
if ( j < 0 ) {
|
||||
j = 0;
|
||||
} else if ( j > 255 ) {
|
||||
j = 255;
|
||||
}
|
||||
data[i] = j;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_InvertAlpha
|
||||
=================
|
||||
*/
|
||||
static void R_InvertAlpha( byte *data, int width, int height ) {
|
||||
int i;
|
||||
int c;
|
||||
|
||||
c = width * height* 4;
|
||||
|
||||
for ( i = 0 ; i < c ; i+=4 ) {
|
||||
data[i+3] = 255 - data[i+3];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_InvertColor
|
||||
=================
|
||||
*/
|
||||
static void R_InvertColor( byte *data, int width, int height ) {
|
||||
int i;
|
||||
int c;
|
||||
|
||||
c = width * height* 4;
|
||||
|
||||
for ( i = 0 ; i < c ; i+=4 ) {
|
||||
data[i+0] = 255 - data[i+0];
|
||||
data[i+1] = 255 - data[i+1];
|
||||
data[i+2] = 255 - data[i+2];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
R_AddNormalMaps
|
||||
|
||||
===================
|
||||
*/
|
||||
static void R_AddNormalMaps( byte *data1, int width1, int height1, byte *data2, int width2, int height2 ) {
|
||||
int i, j;
|
||||
byte *newMap;
|
||||
|
||||
// resample pic2 to the same size as pic1
|
||||
if ( width2 != width1 || height2 != height1 ) {
|
||||
newMap = R_Dropsample( data2, width2, height2, width1, height1 );
|
||||
data2 = newMap;
|
||||
} else {
|
||||
newMap = NULL;
|
||||
}
|
||||
|
||||
// add the normal change from the second and renormalize
|
||||
for ( i = 0 ; i < height1 ; i++ ) {
|
||||
for ( j = 0 ; j < width1 ; j++ ) {
|
||||
byte *d1, *d2;
|
||||
idVec3 n;
|
||||
float len;
|
||||
|
||||
d1 = data1 + ( i * width1 + j ) * 4;
|
||||
d2 = data2 + ( i * width1 + j ) * 4;
|
||||
|
||||
n[0] = ( d1[0] - 128 ) / 127.0;
|
||||
n[1] = ( d1[1] - 128 ) / 127.0;
|
||||
n[2] = ( d1[2] - 128 ) / 127.0;
|
||||
|
||||
// There are some normal maps that blend to 0,0,0 at the edges
|
||||
// this screws up compression, so we try to correct that here by instead fading it to 0,0,1
|
||||
len = n.LengthFast();
|
||||
if ( len < 1.0f ) {
|
||||
n[2] = idMath::Sqrt(1.0 - (n[0]*n[0]) - (n[1]*n[1]));
|
||||
}
|
||||
|
||||
n[0] += ( d2[0] - 128 ) / 127.0;
|
||||
n[1] += ( d2[1] - 128 ) / 127.0;
|
||||
n.Normalize();
|
||||
|
||||
d1[0] = (byte)(n[0] * 127 + 128);
|
||||
d1[1] = (byte)(n[1] * 127 + 128);
|
||||
d1[2] = (byte)(n[2] * 127 + 128);
|
||||
d1[3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
if ( newMap ) {
|
||||
R_StaticFree( newMap );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
R_SmoothNormalMap
|
||||
================
|
||||
*/
|
||||
static void R_SmoothNormalMap( byte *data, int width, int height ) {
|
||||
byte *orig;
|
||||
int i, j, k, l;
|
||||
idVec3 normal;
|
||||
byte *out;
|
||||
static float factors[3][3] = {
|
||||
{ 1, 1, 1 },
|
||||
{ 1, 1, 1 },
|
||||
{ 1, 1, 1 }
|
||||
};
|
||||
|
||||
orig = (byte *)R_StaticAlloc( width * height * 4 );
|
||||
memcpy( orig, data, width * height * 4 );
|
||||
|
||||
for ( i = 0 ; i < width ; i++ ) {
|
||||
for ( j = 0 ; j < height ; j++ ) {
|
||||
normal = vec3_origin;
|
||||
for ( k = -1 ; k < 2 ; k++ ) {
|
||||
for ( l = -1 ; l < 2 ; l++ ) {
|
||||
byte *in;
|
||||
|
||||
in = orig + ( ((j+l)&(height-1))*width + ((i+k)&(width-1)) ) * 4;
|
||||
|
||||
// ignore 000 and -1 -1 -1
|
||||
if ( in[0] == 0 && in[1] == 0 && in[2] == 0 ) {
|
||||
continue;
|
||||
}
|
||||
if ( in[0] == 128 && in[1] == 128 && in[2] == 128 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
normal[0] += factors[k+1][l+1] * ( in[0] - 128 );
|
||||
normal[1] += factors[k+1][l+1] * ( in[1] - 128 );
|
||||
normal[2] += factors[k+1][l+1] * ( in[2] - 128 );
|
||||
}
|
||||
}
|
||||
normal.Normalize();
|
||||
out = data + ( j * width + i ) * 4;
|
||||
out[0] = (byte)(128 + 127 * normal[0]);
|
||||
out[1] = (byte)(128 + 127 * normal[1]);
|
||||
out[2] = (byte)(128 + 127 * normal[2]);
|
||||
}
|
||||
}
|
||||
|
||||
R_StaticFree( orig );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
R_ImageAdd
|
||||
|
||||
===================
|
||||
*/
|
||||
static void R_ImageAdd( byte *data1, int width1, int height1, byte *data2, int width2, int height2 ) {
|
||||
int i, j;
|
||||
int c;
|
||||
byte *newMap;
|
||||
|
||||
// resample pic2 to the same size as pic1
|
||||
if ( width2 != width1 || height2 != height1 ) {
|
||||
newMap = R_Dropsample( data2, width2, height2, width1, height1 );
|
||||
data2 = newMap;
|
||||
} else {
|
||||
newMap = NULL;
|
||||
}
|
||||
|
||||
|
||||
c = width1 * height1 * 4;
|
||||
|
||||
for ( i = 0 ; i < c ; i++ ) {
|
||||
j = data1[i] + data2[i];
|
||||
if ( j > 255 ) {
|
||||
j = 255;
|
||||
}
|
||||
data1[i] = j;
|
||||
}
|
||||
|
||||
if ( newMap ) {
|
||||
R_StaticFree( newMap );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we build a canonical token form of the image program here
|
||||
static char parseBuffer[MAX_IMAGE_NAME];
|
||||
|
||||
/*
|
||||
===================
|
||||
AppendToken
|
||||
===================
|
||||
*/
|
||||
static void AppendToken( idToken &token ) {
|
||||
// add a leading space if not at the beginning
|
||||
if ( parseBuffer[0] ) {
|
||||
idStr::Append( parseBuffer, MAX_IMAGE_NAME, " " );
|
||||
}
|
||||
idStr::Append( parseBuffer, MAX_IMAGE_NAME, token.c_str() );
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
MatchAndAppendToken
|
||||
===================
|
||||
*/
|
||||
static void MatchAndAppendToken( idLexer &src, const char *match ) {
|
||||
if ( !src.ExpectTokenString( match ) ) {
|
||||
return;
|
||||
}
|
||||
// a matched token won't need a leading space
|
||||
idStr::Append( parseBuffer, MAX_IMAGE_NAME, match );
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
R_ParseImageProgram_r
|
||||
|
||||
If pic is NULL, the timestamps will be filled in, but no image will be generated
|
||||
If both pic and timestamps are NULL, it will just advance past it, which can be
|
||||
used to parse an image program from a text stream.
|
||||
===================
|
||||
*/
|
||||
static bool R_ParseImageProgram_r( idLexer &src, byte **pic, int *width, int *height,
|
||||
ID_TIME_T *timestamps, textureDepth_t *depth ) {
|
||||
idToken token;
|
||||
float scale;
|
||||
ID_TIME_T timestamp;
|
||||
|
||||
src.ReadToken( &token );
|
||||
AppendToken( token );
|
||||
|
||||
if ( !token.Icmp( "heightmap" ) ) {
|
||||
MatchAndAppendToken( src, "(" );
|
||||
|
||||
if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, "," );
|
||||
|
||||
src.ReadToken( &token );
|
||||
AppendToken( token );
|
||||
scale = token.GetFloatValue();
|
||||
|
||||
// process it
|
||||
if ( pic ) {
|
||||
R_HeightmapToNormalMap( *pic, *width, *height, scale );
|
||||
if ( depth ) {
|
||||
*depth = TD_BUMP;
|
||||
}
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, ")" );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "addnormals" ) ) {
|
||||
byte *pic2;
|
||||
int width2, height2;
|
||||
|
||||
MatchAndAppendToken( src, "(" );
|
||||
|
||||
if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, "," );
|
||||
|
||||
if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, depth ) ) {
|
||||
if ( pic ) {
|
||||
R_StaticFree( *pic );
|
||||
*pic = NULL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// process it
|
||||
if ( pic ) {
|
||||
R_AddNormalMaps( *pic, *width, *height, pic2, width2, height2 );
|
||||
R_StaticFree( pic2 );
|
||||
if ( depth ) {
|
||||
*depth = TD_BUMP;
|
||||
}
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, ")" );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "smoothnormals" ) ) {
|
||||
MatchAndAppendToken( src, "(" );
|
||||
|
||||
if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( pic ) {
|
||||
R_SmoothNormalMap( *pic, *width, *height );
|
||||
if ( depth ) {
|
||||
*depth = TD_BUMP;
|
||||
}
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, ")" );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "add" ) ) {
|
||||
byte *pic2;
|
||||
int width2, height2;
|
||||
|
||||
MatchAndAppendToken( src, "(" );
|
||||
|
||||
if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, "," );
|
||||
|
||||
if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, depth ) ) {
|
||||
if ( pic ) {
|
||||
R_StaticFree( *pic );
|
||||
*pic = NULL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// process it
|
||||
if ( pic ) {
|
||||
R_ImageAdd( *pic, *width, *height, pic2, width2, height2 );
|
||||
R_StaticFree( pic2 );
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, ")" );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "scale" ) ) {
|
||||
float scale[4];
|
||||
int i;
|
||||
|
||||
MatchAndAppendToken( src, "(" );
|
||||
|
||||
R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
|
||||
|
||||
for ( i = 0 ; i < 4 ; i++ ) {
|
||||
MatchAndAppendToken( src, "," );
|
||||
src.ReadToken( &token );
|
||||
AppendToken( token );
|
||||
scale[i] = token.GetFloatValue();
|
||||
}
|
||||
|
||||
// process it
|
||||
if ( pic ) {
|
||||
R_ImageScale( *pic, *width, *height, scale );
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, ")" );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "invertAlpha" ) ) {
|
||||
MatchAndAppendToken( src, "(" );
|
||||
|
||||
R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
|
||||
|
||||
// process it
|
||||
if ( pic ) {
|
||||
R_InvertAlpha( *pic, *width, *height );
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, ")" );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "invertColor" ) ) {
|
||||
MatchAndAppendToken( src, "(" );
|
||||
|
||||
R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
|
||||
|
||||
// process it
|
||||
if ( pic ) {
|
||||
R_InvertColor( *pic, *width, *height );
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, ")" );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "makeIntensity" ) ) {
|
||||
int i;
|
||||
|
||||
MatchAndAppendToken( src, "(" );
|
||||
|
||||
R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
|
||||
|
||||
// copy red to green, blue, and alpha
|
||||
if ( pic ) {
|
||||
int c;
|
||||
c = *width * *height * 4;
|
||||
for ( i = 0 ; i < c ; i+=4 ) {
|
||||
(*pic)[i+1] =
|
||||
(*pic)[i+2] =
|
||||
(*pic)[i+3] = (*pic)[i];
|
||||
}
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, ")" );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !token.Icmp( "makeAlpha" ) ) {
|
||||
int i;
|
||||
|
||||
MatchAndAppendToken( src, "(" );
|
||||
|
||||
R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
|
||||
|
||||
// average RGB into alpha, then set RGB to white
|
||||
if ( pic ) {
|
||||
int c;
|
||||
c = *width * *height * 4;
|
||||
for ( i = 0 ; i < c ; i+=4 ) {
|
||||
(*pic)[i+3] = ( (*pic)[i+0] + (*pic)[i+1] + (*pic)[i+2] ) / 3;
|
||||
(*pic)[i+0] =
|
||||
(*pic)[i+1] =
|
||||
(*pic)[i+2] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
MatchAndAppendToken( src, ")" );
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we are just parsing instead of loading or checking,
|
||||
// don't do the R_LoadImage
|
||||
if ( !timestamps && !pic ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// load it as an image
|
||||
R_LoadImage( token.c_str(), pic, width, height, ×tamp, true );
|
||||
|
||||
if ( timestamp == -1 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// add this to the timestamp
|
||||
if ( timestamps ) {
|
||||
if ( timestamp > *timestamps ) {
|
||||
*timestamps = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
R_LoadImageProgram
|
||||
===================
|
||||
*/
|
||||
void R_LoadImageProgram( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamps, textureDepth_t *depth ) {
|
||||
idLexer src;
|
||||
|
||||
src.LoadMemory( name, strlen(name), name );
|
||||
src.SetFlags( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
|
||||
|
||||
parseBuffer[0] = 0;
|
||||
if ( timestamps ) {
|
||||
*timestamps = 0;
|
||||
}
|
||||
|
||||
R_ParseImageProgram_r( src, pic, width, height, timestamps, depth );
|
||||
|
||||
src.FreeSource();
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
R_ParsePastImageProgram
|
||||
===================
|
||||
*/
|
||||
const char *R_ParsePastImageProgram( idLexer &src ) {
|
||||
parseBuffer[0] = 0;
|
||||
R_ParseImageProgram_r( src, NULL, NULL, NULL, NULL, NULL );
|
||||
return parseBuffer;
|
||||
}
|
||||
|
||||
1308
neo/renderer/Interaction.cpp
Normal file
1308
neo/renderer/Interaction.cpp
Normal file
File diff suppressed because it is too large
Load Diff
184
neo/renderer/Interaction.h
Normal file
184
neo/renderer/Interaction.h
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __INTERACTION_H__
|
||||
#define __INTERACTION_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Interaction between entityDef surfaces and a lightDef.
|
||||
|
||||
Interactions with no lightTris and no shadowTris are still
|
||||
valid, because they show that a given entityDef / lightDef
|
||||
do not interact, even though they share one or more areas.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
#define LIGHT_TRIS_DEFERRED ((srfTriangles_t *)-1)
|
||||
#define LIGHT_CULL_ALL_FRONT ((byte *)-1)
|
||||
#define LIGHT_CLIP_EPSILON 0.1f
|
||||
|
||||
|
||||
typedef struct {
|
||||
// For each triangle a byte set to 1 if facing the light origin.
|
||||
byte * facing;
|
||||
|
||||
// For each vertex a byte with the bits [0-5] set if the
|
||||
// vertex is at the back side of the corresponding clip plane.
|
||||
// If the 'cullBits' pointer equals LIGHT_CULL_ALL_FRONT all
|
||||
// vertices are at the front of all the clip planes.
|
||||
byte * cullBits;
|
||||
|
||||
// Clip planes in surface space used to calculate the cull bits.
|
||||
idPlane localClipPlanes[6];
|
||||
} srfCullInfo_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
// if lightTris == LIGHT_TRIS_DEFERRED, then the calculation of the
|
||||
// lightTris has been deferred, and must be done if ambientTris is visible
|
||||
srfTriangles_t * lightTris;
|
||||
|
||||
// shadow volume triangle surface
|
||||
srfTriangles_t * shadowTris;
|
||||
|
||||
// so we can check ambientViewCount before adding lightTris, and get
|
||||
// at the shared vertex and possibly shadowVertex caches
|
||||
srfTriangles_t * ambientTris;
|
||||
|
||||
const idMaterial * shader;
|
||||
|
||||
int expCulled; // only for the experimental shadow buffer renderer
|
||||
|
||||
srfCullInfo_t cullInfo;
|
||||
} surfaceInteraction_t;
|
||||
|
||||
|
||||
typedef struct areaNumRef_s {
|
||||
struct areaNumRef_s * next;
|
||||
int areaNum;
|
||||
} areaNumRef_t;
|
||||
|
||||
|
||||
class idRenderEntityLocal;
|
||||
class idRenderLightLocal;
|
||||
|
||||
class idInteraction {
|
||||
public:
|
||||
// this may be 0 if the light and entity do not actually intersect
|
||||
// -1 = an untested interaction
|
||||
int numSurfaces;
|
||||
|
||||
// if there is a whole-entity optimized shadow hull, it will
|
||||
// be present as a surfaceInteraction_t with a NULL ambientTris, but
|
||||
// possibly having a shader to specify the shadow sorting order
|
||||
surfaceInteraction_t * surfaces;
|
||||
|
||||
// get space from here, if NULL, it is a pre-generated shadow volume from dmap
|
||||
idRenderEntityLocal * entityDef;
|
||||
idRenderLightLocal * lightDef;
|
||||
|
||||
idInteraction * lightNext; // for lightDef chains
|
||||
idInteraction * lightPrev;
|
||||
idInteraction * entityNext; // for entityDef chains
|
||||
idInteraction * entityPrev;
|
||||
|
||||
public:
|
||||
idInteraction( void );
|
||||
|
||||
// because these are generated and freed each game tic for active elements all
|
||||
// over the world, we use a custom pool allocater to avoid memory allocation overhead
|
||||
// and fragmentation
|
||||
static idInteraction * AllocAndLink( idRenderEntityLocal *edef, idRenderLightLocal *ldef );
|
||||
|
||||
// unlinks from the entity and light, frees all surfaceInteractions,
|
||||
// and puts it back on the free list
|
||||
void UnlinkAndFree( void );
|
||||
|
||||
// free the interaction surfaces
|
||||
void FreeSurfaces( void );
|
||||
|
||||
// makes the interaction empty for when the light and entity do not actually intersect
|
||||
// all empty interactions are linked at the end of the light's and entity's interaction list
|
||||
void MakeEmpty( void );
|
||||
|
||||
// returns true if the interaction is empty
|
||||
bool IsEmpty( void ) const { return ( numSurfaces == 0 ); }
|
||||
|
||||
// returns true if the interaction is not yet completely created
|
||||
bool IsDeferred( void ) const { return ( numSurfaces == -1 ); }
|
||||
|
||||
// returns true if the interaction has shadows
|
||||
bool HasShadows( void ) const;
|
||||
|
||||
// counts up the memory used by all the surfaceInteractions, which
|
||||
// will be used to determine when we need to start purging old interactions
|
||||
int MemoryUsed( void );
|
||||
|
||||
// makes sure all necessary light surfaces and shadow surfaces are created, and
|
||||
// calls R_LinkLightSurf() for each one
|
||||
void AddActiveInteraction( void );
|
||||
|
||||
private:
|
||||
enum {
|
||||
FRUSTUM_UNINITIALIZED,
|
||||
FRUSTUM_INVALID,
|
||||
FRUSTUM_VALID,
|
||||
FRUSTUM_VALIDAREAS,
|
||||
} frustumState;
|
||||
idFrustum frustum; // frustum which contains the interaction
|
||||
areaNumRef_t * frustumAreas; // numbers of the areas the frustum touches
|
||||
|
||||
int dynamicModelFrameCount; // so we can tell if a callback model animated
|
||||
|
||||
private:
|
||||
// actually create the interaction
|
||||
void CreateInteraction( const idRenderModel *model );
|
||||
|
||||
// unlink from entity and light lists
|
||||
void Unlink( void );
|
||||
|
||||
// try to determine if the entire interaction, including shadows, is guaranteed
|
||||
// to be outside the view frustum
|
||||
bool CullInteractionByViewFrustum( const idFrustum &viewFrustum );
|
||||
|
||||
// determine the minimum scissor rect that will include the interaction shadows
|
||||
// projected to the bounds of the light
|
||||
idScreenRect CalcInteractionScissorRectangle( const idFrustum &viewFrustum );
|
||||
};
|
||||
|
||||
|
||||
void R_CalcInteractionFacing( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo );
|
||||
void R_CalcInteractionCullBits( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo );
|
||||
void R_FreeInteractionCullInfo( srfCullInfo_t &cullInfo );
|
||||
|
||||
void R_ShowInteractionMemory_f( const idCmdArgs &args );
|
||||
|
||||
#endif /* !__INTERACTION_H__ */
|
||||
2736
neo/renderer/Material.cpp
Normal file
2736
neo/renderer/Material.cpp
Normal file
File diff suppressed because it is too large
Load Diff
691
neo/renderer/Material.h
Normal file
691
neo/renderer/Material.h
Normal file
@@ -0,0 +1,691 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __MATERIAL_H__
|
||||
#define __MATERIAL_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Material
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idImage;
|
||||
class idCinematic;
|
||||
class idUserInterface;
|
||||
class idMegaTexture;
|
||||
|
||||
// moved from image.h for default parm
|
||||
typedef enum {
|
||||
TF_LINEAR,
|
||||
TF_NEAREST,
|
||||
TF_DEFAULT // use the user-specified r_textureFilter
|
||||
} textureFilter_t;
|
||||
|
||||
typedef enum {
|
||||
TR_REPEAT,
|
||||
TR_CLAMP,
|
||||
TR_CLAMP_TO_BORDER, // this should replace TR_CLAMP_TO_ZERO and TR_CLAMP_TO_ZERO_ALPHA,
|
||||
// but I don't want to risk changing it right now
|
||||
TR_CLAMP_TO_ZERO, // guarantee 0,0,0,255 edge for projected textures,
|
||||
// set AFTER image format selection
|
||||
TR_CLAMP_TO_ZERO_ALPHA // guarantee 0 alpha edge for projected textures,
|
||||
// set AFTER image format selection
|
||||
} textureRepeat_t;
|
||||
|
||||
typedef struct {
|
||||
int stayTime; // msec for no change
|
||||
int fadeTime; // msec to fade vertex colors over
|
||||
float start[4]; // vertex color at spawn (possibly out of 0.0 - 1.0 range, will clamp after calc)
|
||||
float end[4]; // vertex color at fade-out (possibly out of 0.0 - 1.0 range, will clamp after calc)
|
||||
} decalInfo_t;
|
||||
|
||||
typedef enum {
|
||||
DFRM_NONE,
|
||||
DFRM_SPRITE,
|
||||
DFRM_TUBE,
|
||||
DFRM_FLARE,
|
||||
DFRM_EXPAND,
|
||||
DFRM_MOVE,
|
||||
DFRM_EYEBALL,
|
||||
DFRM_PARTICLE,
|
||||
DFRM_PARTICLE2,
|
||||
DFRM_TURB
|
||||
} deform_t;
|
||||
|
||||
typedef enum {
|
||||
DI_STATIC,
|
||||
DI_SCRATCH, // video, screen wipe, etc
|
||||
DI_CUBE_RENDER,
|
||||
DI_MIRROR_RENDER,
|
||||
DI_XRAY_RENDER,
|
||||
DI_REMOTE_RENDER
|
||||
} dynamicidImage_t;
|
||||
|
||||
// note: keep opNames[] in sync with changes
|
||||
typedef enum {
|
||||
OP_TYPE_ADD,
|
||||
OP_TYPE_SUBTRACT,
|
||||
OP_TYPE_MULTIPLY,
|
||||
OP_TYPE_DIVIDE,
|
||||
OP_TYPE_MOD,
|
||||
OP_TYPE_TABLE,
|
||||
OP_TYPE_GT,
|
||||
OP_TYPE_GE,
|
||||
OP_TYPE_LT,
|
||||
OP_TYPE_LE,
|
||||
OP_TYPE_EQ,
|
||||
OP_TYPE_NE,
|
||||
OP_TYPE_AND,
|
||||
OP_TYPE_OR,
|
||||
OP_TYPE_SOUND
|
||||
} expOpType_t;
|
||||
|
||||
typedef enum {
|
||||
EXP_REG_TIME,
|
||||
|
||||
EXP_REG_PARM0,
|
||||
EXP_REG_PARM1,
|
||||
EXP_REG_PARM2,
|
||||
EXP_REG_PARM3,
|
||||
EXP_REG_PARM4,
|
||||
EXP_REG_PARM5,
|
||||
EXP_REG_PARM6,
|
||||
EXP_REG_PARM7,
|
||||
EXP_REG_PARM8,
|
||||
EXP_REG_PARM9,
|
||||
EXP_REG_PARM10,
|
||||
EXP_REG_PARM11,
|
||||
|
||||
EXP_REG_GLOBAL0,
|
||||
EXP_REG_GLOBAL1,
|
||||
EXP_REG_GLOBAL2,
|
||||
EXP_REG_GLOBAL3,
|
||||
EXP_REG_GLOBAL4,
|
||||
EXP_REG_GLOBAL5,
|
||||
EXP_REG_GLOBAL6,
|
||||
EXP_REG_GLOBAL7,
|
||||
|
||||
EXP_REG_NUM_PREDEFINED
|
||||
} expRegister_t;
|
||||
|
||||
typedef struct {
|
||||
expOpType_t opType;
|
||||
int a, b, c;
|
||||
} expOp_t;
|
||||
|
||||
typedef struct {
|
||||
int registers[4];
|
||||
} colorStage_t;
|
||||
|
||||
typedef enum {
|
||||
TG_EXPLICIT,
|
||||
TG_DIFFUSE_CUBE,
|
||||
TG_REFLECT_CUBE,
|
||||
TG_SKYBOX_CUBE,
|
||||
TG_WOBBLESKY_CUBE,
|
||||
TG_SCREEN, // screen aligned, for mirrorRenders and screen space temporaries
|
||||
TG_SCREEN2,
|
||||
TG_GLASSWARP
|
||||
} texgen_t;
|
||||
|
||||
typedef struct {
|
||||
idCinematic * cinematic;
|
||||
idImage * image;
|
||||
texgen_t texgen;
|
||||
bool hasMatrix;
|
||||
int matrix[2][3]; // we only allow a subset of the full projection matrix
|
||||
|
||||
// dynamic image variables
|
||||
dynamicidImage_t dynamic;
|
||||
int width, height;
|
||||
int dynamicFrameCount;
|
||||
} textureStage_t;
|
||||
|
||||
// the order BUMP / DIFFUSE / SPECULAR is necessary for interactions to draw correctly on low end cards
|
||||
typedef enum {
|
||||
SL_AMBIENT, // execute after lighting
|
||||
SL_BUMP,
|
||||
SL_DIFFUSE,
|
||||
SL_SPECULAR
|
||||
} stageLighting_t;
|
||||
|
||||
// cross-blended terrain textures need to modulate the color by
|
||||
// the vertex color to smoothly blend between two textures
|
||||
typedef enum {
|
||||
SVC_IGNORE,
|
||||
SVC_MODULATE,
|
||||
SVC_INVERSE_MODULATE
|
||||
} stageVertexColor_t;
|
||||
|
||||
static const int MAX_FRAGMENT_IMAGES = 8;
|
||||
static const int MAX_VERTEX_PARMS = 4;
|
||||
|
||||
typedef struct {
|
||||
int vertexProgram;
|
||||
int numVertexParms;
|
||||
int vertexParms[MAX_VERTEX_PARMS][4]; // evaluated register indexes
|
||||
|
||||
int fragmentProgram;
|
||||
int numFragmentProgramImages;
|
||||
idImage * fragmentProgramImages[MAX_FRAGMENT_IMAGES];
|
||||
|
||||
idMegaTexture *megaTexture; // handles all the binding and parameter setting
|
||||
} newShaderStage_t;
|
||||
|
||||
typedef struct {
|
||||
int conditionRegister; // if registers[conditionRegister] == 0, skip stage
|
||||
stageLighting_t lighting; // determines which passes interact with lights
|
||||
int drawStateBits;
|
||||
colorStage_t color;
|
||||
bool hasAlphaTest;
|
||||
int alphaTestRegister;
|
||||
textureStage_t texture;
|
||||
stageVertexColor_t vertexColor;
|
||||
bool ignoreAlphaTest; // this stage should act as translucent, even
|
||||
// if the surface is alpha tested
|
||||
float privatePolygonOffset; // a per-stage polygon offset
|
||||
|
||||
newShaderStage_t *newStage; // vertex / fragment program based stage
|
||||
} shaderStage_t;
|
||||
|
||||
typedef enum {
|
||||
MC_BAD,
|
||||
MC_OPAQUE, // completely fills the triangle, will have black drawn on fillDepthBuffer
|
||||
MC_PERFORATED, // may have alpha tested holes
|
||||
MC_TRANSLUCENT // blended with background
|
||||
} materialCoverage_t;
|
||||
|
||||
typedef enum {
|
||||
SS_SUBVIEW = -3, // mirrors, viewscreens, etc
|
||||
SS_GUI = -2, // guis
|
||||
SS_BAD = -1,
|
||||
SS_OPAQUE, // opaque
|
||||
|
||||
SS_PORTAL_SKY,
|
||||
|
||||
SS_DECAL, // scorch marks, etc.
|
||||
|
||||
SS_FAR,
|
||||
SS_MEDIUM, // normal translucent
|
||||
SS_CLOSE,
|
||||
|
||||
SS_ALMOST_NEAREST, // gun smoke puffs
|
||||
|
||||
SS_NEAREST, // screen blood blobs
|
||||
|
||||
SS_POST_PROCESS = 100 // after a screen copy to texture
|
||||
} materialSort_t;
|
||||
|
||||
typedef enum {
|
||||
CT_FRONT_SIDED,
|
||||
CT_BACK_SIDED,
|
||||
CT_TWO_SIDED
|
||||
} cullType_t;
|
||||
|
||||
// these don't effect per-material storage, so they can be very large
|
||||
const int MAX_SHADER_STAGES = 256;
|
||||
|
||||
const int MAX_TEXGEN_REGISTERS = 4;
|
||||
|
||||
const int MAX_ENTITY_SHADER_PARMS = 12;
|
||||
|
||||
// material flags
|
||||
typedef enum {
|
||||
MF_DEFAULTED = BIT(0),
|
||||
MF_POLYGONOFFSET = BIT(1),
|
||||
MF_NOSHADOWS = BIT(2),
|
||||
MF_FORCESHADOWS = BIT(3),
|
||||
MF_NOSELFSHADOW = BIT(4),
|
||||
MF_NOPORTALFOG = BIT(5), // this fog volume won't ever consider a portal fogged out
|
||||
MF_EDITOR_VISIBLE = BIT(6) // in use (visible) per editor
|
||||
} materialFlags_t;
|
||||
|
||||
// contents flags, NOTE: make sure to keep the defines in doom_defs.script up to date with these!
|
||||
typedef enum {
|
||||
CONTENTS_SOLID = BIT(0), // an eye is never valid in a solid
|
||||
CONTENTS_OPAQUE = BIT(1), // blocks visibility (for ai)
|
||||
CONTENTS_WATER = BIT(2), // used for water
|
||||
CONTENTS_PLAYERCLIP = BIT(3), // solid to players
|
||||
CONTENTS_MONSTERCLIP = BIT(4), // solid to monsters
|
||||
CONTENTS_MOVEABLECLIP = BIT(5), // solid to moveable entities
|
||||
CONTENTS_IKCLIP = BIT(6), // solid to IK
|
||||
CONTENTS_BLOOD = BIT(7), // used to detect blood decals
|
||||
CONTENTS_BODY = BIT(8), // used for actors
|
||||
CONTENTS_PROJECTILE = BIT(9), // used for projectiles
|
||||
CONTENTS_CORPSE = BIT(10), // used for dead bodies
|
||||
CONTENTS_RENDERMODEL = BIT(11), // used for render models for collision detection
|
||||
CONTENTS_TRIGGER = BIT(12), // used for triggers
|
||||
CONTENTS_AAS_SOLID = BIT(13), // solid for AAS
|
||||
CONTENTS_AAS_OBSTACLE = BIT(14), // used to compile an obstacle into AAS that can be enabled/disabled
|
||||
CONTENTS_FLASHLIGHT_TRIGGER = BIT(15), // used for triggers that are activated by the flashlight
|
||||
|
||||
// contents used by utils
|
||||
CONTENTS_AREAPORTAL = BIT(20), // portal separating renderer areas
|
||||
CONTENTS_NOCSG = BIT(21), // don't cut this brush with CSG operations in the editor
|
||||
|
||||
CONTENTS_REMOVE_UTIL = ~(CONTENTS_AREAPORTAL|CONTENTS_NOCSG)
|
||||
} contentsFlags_t;
|
||||
|
||||
// surface types
|
||||
const int NUM_SURFACE_BITS = 4;
|
||||
const int MAX_SURFACE_TYPES = 1 << NUM_SURFACE_BITS;
|
||||
|
||||
typedef enum {
|
||||
SURFTYPE_NONE, // default type
|
||||
SURFTYPE_METAL,
|
||||
SURFTYPE_STONE,
|
||||
SURFTYPE_FLESH,
|
||||
SURFTYPE_WOOD,
|
||||
SURFTYPE_CARDBOARD,
|
||||
SURFTYPE_LIQUID,
|
||||
SURFTYPE_GLASS,
|
||||
SURFTYPE_PLASTIC,
|
||||
SURFTYPE_RICOCHET,
|
||||
SURFTYPE_10,
|
||||
SURFTYPE_11,
|
||||
SURFTYPE_12,
|
||||
SURFTYPE_13,
|
||||
SURFTYPE_14,
|
||||
SURFTYPE_15
|
||||
} surfTypes_t;
|
||||
|
||||
// surface flags
|
||||
typedef enum {
|
||||
SURF_TYPE_BIT0 = BIT(0), // encodes the material type (metal, flesh, concrete, etc.)
|
||||
SURF_TYPE_BIT1 = BIT(1), // "
|
||||
SURF_TYPE_BIT2 = BIT(2), // "
|
||||
SURF_TYPE_BIT3 = BIT(3), // "
|
||||
SURF_TYPE_MASK = ( 1 << NUM_SURFACE_BITS ) - 1,
|
||||
|
||||
SURF_NODAMAGE = BIT(4), // never give falling damage
|
||||
SURF_SLICK = BIT(5), // effects game physics
|
||||
SURF_COLLISION = BIT(6), // collision surface
|
||||
SURF_LADDER = BIT(7), // player can climb up this surface
|
||||
SURF_NOIMPACT = BIT(8), // don't make missile explosions
|
||||
SURF_NOSTEPS = BIT(9), // no footstep sounds
|
||||
SURF_DISCRETE = BIT(10), // not clipped or merged by utilities
|
||||
SURF_NOFRAGMENT = BIT(11), // dmap won't cut surface at each bsp boundary
|
||||
SURF_NULLNORMAL = BIT(12) // renderbump will draw this surface as 0x80 0x80 0x80, which
|
||||
// won't collect light from any angle
|
||||
} surfaceFlags_t;
|
||||
|
||||
class idSoundEmitter;
|
||||
|
||||
class idMaterial : public idDecl {
|
||||
public:
|
||||
idMaterial();
|
||||
virtual ~idMaterial();
|
||||
|
||||
virtual size_t Size( void ) const;
|
||||
virtual bool SetDefaultText( void );
|
||||
virtual const char *DefaultDefinition( void ) const;
|
||||
virtual bool Parse( const char *text, const int textLength );
|
||||
virtual void FreeData( void );
|
||||
virtual void Print( void ) const;
|
||||
|
||||
//BSM Nerve: Added for material editor
|
||||
bool Save( const char *fileName = NULL );
|
||||
|
||||
// returns the internal image name for stage 0, which can be used
|
||||
// for the renderer CaptureRenderToImage() call
|
||||
// I'm not really sure why this needs to be virtual...
|
||||
virtual const char *ImageName( void ) const;
|
||||
|
||||
void ReloadImages( bool force ) const;
|
||||
|
||||
// returns number of stages this material contains
|
||||
const int GetNumStages( void ) const { return numStages; }
|
||||
|
||||
// get a specific stage
|
||||
const shaderStage_t *GetStage( const int index ) const { assert(index >= 0 && index < numStages); return &stages[index]; }
|
||||
|
||||
// get the first bump map stage, or NULL if not present.
|
||||
// used for bumpy-specular
|
||||
const shaderStage_t *GetBumpStage( void ) const;
|
||||
|
||||
// returns true if the material will draw anything at all. Triggers, portals,
|
||||
// etc, will not have anything to draw. A not drawn surface can still castShadow,
|
||||
// which can be used to make a simplified shadow hull for a complex object set
|
||||
// as noShadow
|
||||
bool IsDrawn( void ) const { return ( numStages > 0 || entityGui != 0 || gui != NULL ); }
|
||||
|
||||
// returns true if the material will draw any non light interaction stages
|
||||
bool HasAmbient( void ) const { return ( numAmbientStages > 0 ); }
|
||||
|
||||
// returns true if material has a gui
|
||||
bool HasGui( void ) const { return ( entityGui != 0 || gui != NULL ); }
|
||||
|
||||
// returns true if the material will generate another view, either as
|
||||
// a mirror or dynamic rendered image
|
||||
bool HasSubview( void ) const { return hasSubview; }
|
||||
|
||||
// returns true if the material will generate shadows, not making a
|
||||
// distinction between global and no-self shadows
|
||||
bool SurfaceCastsShadow( void ) const { return TestMaterialFlag( MF_FORCESHADOWS ) || !TestMaterialFlag( MF_NOSHADOWS ); }
|
||||
|
||||
// returns true if the material will generate interactions with fog/blend lights
|
||||
// All non-translucent surfaces receive fog unless they are explicitly noFog
|
||||
bool ReceivesFog( void ) const { return ( IsDrawn() && !noFog && coverage != MC_TRANSLUCENT ); }
|
||||
|
||||
// returns true if the material will generate interactions with normal lights
|
||||
// Many special effect surfaces don't have any bump/diffuse/specular
|
||||
// stages, and don't interact with lights at all
|
||||
bool ReceivesLighting( void ) const { return numAmbientStages != numStages; }
|
||||
|
||||
// returns true if the material should generate interactions on sides facing away
|
||||
// from light centers, as with noshadow and noselfshadow options
|
||||
bool ReceivesLightingOnBackSides( void ) const { return ( materialFlags & (MF_NOSELFSHADOW|MF_NOSHADOWS) ) != 0; }
|
||||
|
||||
// Standard two-sided triangle rendering won't work with bump map lighting, because
|
||||
// the normal and tangent vectors won't be correct for the back sides. When two
|
||||
// sided lighting is desired. typically for alpha tested surfaces, this is
|
||||
// addressed by having CleanupModelSurfaces() create duplicates of all the triangles
|
||||
// with apropriate order reversal.
|
||||
bool ShouldCreateBackSides( void ) const { return shouldCreateBackSides; }
|
||||
|
||||
// characters and models that are created by a complete renderbump can use a faster
|
||||
// method of tangent and normal vector generation than surfaces which have a flat
|
||||
// renderbump wrapped over them.
|
||||
bool UseUnsmoothedTangents( void ) const { return unsmoothedTangents; }
|
||||
|
||||
// by default, monsters can have blood overlays placed on them, but this can
|
||||
// be overrided on a per-material basis with the "noOverlays" material command.
|
||||
// This will always return false for translucent surfaces
|
||||
bool AllowOverlays( void ) const { return allowOverlays; }
|
||||
|
||||
// MC_OPAQUE, MC_PERFORATED, or MC_TRANSLUCENT, for interaction list linking and
|
||||
// dmap flood filling
|
||||
// The depth buffer will not be filled for MC_TRANSLUCENT surfaces
|
||||
// FIXME: what do nodraw surfaces return?
|
||||
materialCoverage_t Coverage( void ) const { return coverage; }
|
||||
|
||||
// returns true if this material takes precedence over other in coplanar cases
|
||||
bool HasHigherDmapPriority( const idMaterial &other ) const { return ( IsDrawn() && !other.IsDrawn() ) ||
|
||||
( Coverage() < other.Coverage() ); }
|
||||
|
||||
// returns a idUserInterface if it has a global gui, or NULL if no gui
|
||||
idUserInterface * GlobalGui( void ) const { return gui; }
|
||||
|
||||
// a discrete surface will never be merged with other surfaces by dmap, which is
|
||||
// necessary to prevent mutliple gui surfaces, mirrors, autosprites, and some other
|
||||
// special effects from being combined into a single surface
|
||||
// guis, merging sprites or other effects, mirrors and remote views are always discrete
|
||||
bool IsDiscrete( void ) const { return ( entityGui || gui || deform != DFRM_NONE || sort == SS_SUBVIEW ||
|
||||
( surfaceFlags & SURF_DISCRETE ) != 0 ); }
|
||||
|
||||
// Normally, dmap chops each surface by every BSP boundary, then reoptimizes.
|
||||
// For gigantic polygons like sky boxes, this can cause a huge number of planar
|
||||
// triangles that make the optimizer take forever to turn back into a single
|
||||
// triangle. The "noFragment" option causes dmap to only break the polygons at
|
||||
// area boundaries, instead of every BSP boundary. This has the negative effect
|
||||
// of not automatically fixing up interpenetrations, so when this is used, you
|
||||
// should manually make the edges of your sky box exactly meet, instead of poking
|
||||
// into each other.
|
||||
bool NoFragment( void ) const { return ( surfaceFlags & SURF_NOFRAGMENT ) != 0; }
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// light shader specific functions, only called for light entities
|
||||
|
||||
// lightshader option to fill with fog from viewer instead of light from center
|
||||
bool IsFogLight() const { return fogLight; }
|
||||
|
||||
// perform simple blending of the projection, instead of interacting with bumps and textures
|
||||
bool IsBlendLight() const { return blendLight; }
|
||||
|
||||
// an ambient light has non-directional bump mapping and no specular
|
||||
bool IsAmbientLight() const { return ambientLight; }
|
||||
|
||||
// implicitly no-shadows lights (ambients, fogs, etc) will never cast shadows
|
||||
// but individual light entities can also override this value
|
||||
bool LightCastsShadows() const { return TestMaterialFlag( MF_FORCESHADOWS ) ||
|
||||
( !fogLight && !ambientLight && !blendLight && !TestMaterialFlag( MF_NOSHADOWS ) ); }
|
||||
|
||||
// fog lights, blend lights, ambient lights, etc will all have to have interaction
|
||||
// triangles generated for sides facing away from the light as well as those
|
||||
// facing towards the light. It is debatable if noshadow lights should effect back
|
||||
// sides, making everything "noSelfShadow", but that would make noshadow lights
|
||||
// potentially slower than normal lights, which detracts from their optimization
|
||||
// ability, so they currently do not.
|
||||
bool LightEffectsBackSides() const { return fogLight || ambientLight || blendLight; }
|
||||
|
||||
// NULL unless an image is explicitly specified in the shader with "lightFalloffShader <image>"
|
||||
idImage * LightFalloffImage() const { return lightFalloffImage; }
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
// returns the renderbump command line for this shader, or an empty string if not present
|
||||
const char * GetRenderBump() const { return renderBump; };
|
||||
|
||||
// set specific material flag(s)
|
||||
void SetMaterialFlag( const int flag ) const { materialFlags |= flag; }
|
||||
|
||||
// clear specific material flag(s)
|
||||
void ClearMaterialFlag( const int flag ) const { materialFlags &= ~flag; }
|
||||
|
||||
// test for existance of specific material flag(s)
|
||||
bool TestMaterialFlag( const int flag ) const { return ( materialFlags & flag ) != 0; }
|
||||
|
||||
// get content flags
|
||||
const int GetContentFlags( void ) const { return contentFlags; }
|
||||
|
||||
// get surface flags
|
||||
const int GetSurfaceFlags( void ) const { return surfaceFlags; }
|
||||
|
||||
// gets name for surface type (stone, metal, flesh, etc.)
|
||||
const surfTypes_t GetSurfaceType( void ) const { return static_cast<surfTypes_t>( surfaceFlags & SURF_TYPE_MASK ); }
|
||||
|
||||
// get material description
|
||||
const char * GetDescription( void ) const { return desc; }
|
||||
|
||||
// get sort order
|
||||
const float GetSort( void ) const { return sort; }
|
||||
// this is only used by the gui system to force sorting order
|
||||
// on images referenced from tga's instead of materials.
|
||||
// this is done this way as there are 2000 tgas the guis use
|
||||
void SetSort( float s ) const { sort = s; };
|
||||
|
||||
// DFRM_NONE, DFRM_SPRITE, etc
|
||||
deform_t Deform( void ) const { return deform; }
|
||||
|
||||
// flare size, expansion size, etc
|
||||
const int GetDeformRegister( int index ) const { return deformRegisters[index]; }
|
||||
|
||||
// particle system to emit from surface and table for turbulent
|
||||
const idDecl *GetDeformDecl( void ) const { return deformDecl; }
|
||||
|
||||
// currently a surface can only have one unique texgen for all the stages
|
||||
texgen_t Texgen() const;
|
||||
|
||||
// wobble sky parms
|
||||
const int * GetTexGenRegisters( void ) const { return texGenRegisters; }
|
||||
|
||||
// get cull type
|
||||
const cullType_t GetCullType( void ) const { return cullType; }
|
||||
|
||||
float GetEditorAlpha( void ) const { return editorAlpha; }
|
||||
|
||||
int GetEntityGui( void ) const { return entityGui; }
|
||||
|
||||
decalInfo_t GetDecalInfo( void ) const { return decalInfo; }
|
||||
|
||||
// spectrums are used for "invisible writing" that can only be
|
||||
// illuminated by a light of matching spectrum
|
||||
int Spectrum( void ) const { return spectrum; }
|
||||
|
||||
float GetPolygonOffset( void ) const { return polygonOffset; }
|
||||
|
||||
float GetSurfaceArea( void ) const { return surfaceArea; }
|
||||
void AddToSurfaceArea( float area ) { surfaceArea += area; }
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
// returns the length, in milliseconds, of the videoMap on this material,
|
||||
// or zero if it doesn't have one
|
||||
int CinematicLength( void ) const;
|
||||
|
||||
void CloseCinematic( void ) const;
|
||||
|
||||
void ResetCinematicTime( int time ) const;
|
||||
|
||||
void UpdateCinematic( int time ) const;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
// gets an image for the editor to use
|
||||
idImage * GetEditorImage( void ) const;
|
||||
int GetImageWidth( void ) const;
|
||||
int GetImageHeight( void ) const;
|
||||
|
||||
void SetGui( const char *_gui ) const;
|
||||
|
||||
// just for resource tracking
|
||||
void SetImageClassifications( int tag ) const;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
// returns number of registers this material contains
|
||||
const int GetNumRegisters() const { return numRegisters; }
|
||||
|
||||
// regs should point to a float array large enough to hold GetNumRegisters() floats
|
||||
void EvaluateRegisters( float *regs, const float entityParms[MAX_ENTITY_SHADER_PARMS],
|
||||
const struct viewDef_s *view, idSoundEmitter *soundEmitter = NULL ) const;
|
||||
|
||||
// if a material only uses constants (no entityParm or globalparm references), this
|
||||
// will return a pointer to an internal table, and EvaluateRegisters will not need
|
||||
// to be called. If NULL is returned, EvaluateRegisters must be used.
|
||||
const float * ConstantRegisters() const;
|
||||
|
||||
bool SuppressInSubview() const { return suppressInSubview; };
|
||||
bool IsPortalSky() const { return portalSky; };
|
||||
void AddReference();
|
||||
|
||||
private:
|
||||
// parse the entire material
|
||||
void CommonInit();
|
||||
void ParseMaterial( idLexer &src );
|
||||
bool MatchToken( idLexer &src, const char *match );
|
||||
void ParseSort( idLexer &src );
|
||||
void ParseBlend( idLexer &src, shaderStage_t *stage );
|
||||
void ParseVertexParm( idLexer &src, newShaderStage_t *newStage );
|
||||
void ParseFragmentMap( idLexer &src, newShaderStage_t *newStage );
|
||||
void ParseStage( idLexer &src, const textureRepeat_t trpDefault = TR_REPEAT );
|
||||
void ParseDeform( idLexer &src );
|
||||
void ParseDecalInfo( idLexer &src );
|
||||
bool CheckSurfaceParm( idToken *token );
|
||||
int GetExpressionConstant( float f );
|
||||
int GetExpressionTemporary( void );
|
||||
expOp_t * GetExpressionOp( void );
|
||||
int EmitOp( int a, int b, expOpType_t opType );
|
||||
int ParseEmitOp( idLexer &src, int a, expOpType_t opType, int priority );
|
||||
int ParseTerm( idLexer &src );
|
||||
int ParseExpressionPriority( idLexer &src, int priority );
|
||||
int ParseExpression( idLexer &src );
|
||||
void ClearStage( shaderStage_t *ss );
|
||||
int NameToSrcBlendMode( const idStr &name );
|
||||
int NameToDstBlendMode( const idStr &name );
|
||||
void MultiplyTextureMatrix( textureStage_t *ts, int registers[2][3] ); // FIXME: for some reason the const is bad for gcc and Mac
|
||||
void SortInteractionStages();
|
||||
void AddImplicitStages( const textureRepeat_t trpDefault = TR_REPEAT );
|
||||
void CheckForConstantRegisters();
|
||||
|
||||
private:
|
||||
idStr desc; // description
|
||||
idStr renderBump; // renderbump command options, without the "renderbump" at the start
|
||||
|
||||
idImage * lightFalloffImage;
|
||||
|
||||
int entityGui; // draw a gui with the idUserInterface from the renderEntity_t
|
||||
// non zero will draw gui, gui2, or gui3 from renderEnitty_t
|
||||
mutable idUserInterface *gui; // non-custom guis are shared by all users of a material
|
||||
|
||||
bool noFog; // surface does not create fog interactions
|
||||
|
||||
int spectrum; // for invisible writing, used for both lights and surfaces
|
||||
|
||||
float polygonOffset;
|
||||
|
||||
int contentFlags; // content flags
|
||||
int surfaceFlags; // surface flags
|
||||
mutable int materialFlags; // material flags
|
||||
|
||||
decalInfo_t decalInfo;
|
||||
|
||||
|
||||
mutable float sort; // lower numbered shaders draw before higher numbered
|
||||
deform_t deform;
|
||||
int deformRegisters[4]; // numeric parameter for deforms
|
||||
const idDecl *deformDecl; // for surface emitted particle deforms and tables
|
||||
|
||||
int texGenRegisters[MAX_TEXGEN_REGISTERS]; // for wobbleSky
|
||||
|
||||
materialCoverage_t coverage;
|
||||
cullType_t cullType; // CT_FRONT_SIDED, CT_BACK_SIDED, or CT_TWO_SIDED
|
||||
bool shouldCreateBackSides;
|
||||
|
||||
bool fogLight;
|
||||
bool blendLight;
|
||||
bool ambientLight;
|
||||
bool unsmoothedTangents;
|
||||
bool hasSubview; // mirror, remote render, etc
|
||||
bool allowOverlays;
|
||||
|
||||
int numOps;
|
||||
expOp_t * ops; // evaluate to make expressionRegisters
|
||||
|
||||
int numRegisters; //
|
||||
float * expressionRegisters;
|
||||
|
||||
float * constantRegisters; // NULL if ops ever reference globalParms or entityParms
|
||||
|
||||
int numStages;
|
||||
int numAmbientStages;
|
||||
|
||||
shaderStage_t * stages;
|
||||
|
||||
struct mtrParsingData_s *pd; // only used during parsing
|
||||
|
||||
float surfaceArea; // only for listSurfaceAreas
|
||||
|
||||
// we defer loading of the editor image until it is asked for, so the game doesn't load up
|
||||
// all the invisible and uncompressed images.
|
||||
// If editorImage is NULL, it will atempt to load editorImageName, and set editorImage to that or defaultImage
|
||||
idStr editorImageName;
|
||||
mutable idImage * editorImage; // image used for non-shaded preview
|
||||
float editorAlpha;
|
||||
|
||||
bool suppressInSubview;
|
||||
bool portalSky;
|
||||
int refCount;
|
||||
};
|
||||
|
||||
typedef idList<const idMaterial *> idMatList;
|
||||
|
||||
#endif /* !__MATERIAL_H__ */
|
||||
913
neo/renderer/MegaTexture.cpp
Normal file
913
neo/renderer/MegaTexture.cpp
Normal file
@@ -0,0 +1,913 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
idCVar idMegaTexture::r_megaTextureLevel( "r_megaTextureLevel", "0", CVAR_RENDERER | CVAR_INTEGER, "draw only a specific level" );
|
||||
idCVar idMegaTexture::r_showMegaTexture( "r_showMegaTexture", "0", CVAR_RENDERER | CVAR_BOOL, "display all the level images" );
|
||||
idCVar idMegaTexture::r_showMegaTextureLabels( "r_showMegaTextureLabels", "0", CVAR_RENDERER | CVAR_BOOL, "draw colored blocks in each tile" );
|
||||
idCVar idMegaTexture::r_skipMegaTexture( "r_skipMegaTexture", "0", CVAR_RENDERER | CVAR_INTEGER, "only use the lowest level image" );
|
||||
idCVar idMegaTexture::r_terrainScale( "r_terrainScale", "3", CVAR_RENDERER | CVAR_INTEGER, "vertically scale USGS data" );
|
||||
|
||||
/*
|
||||
|
||||
allow sparse population of the upper detail tiles
|
||||
|
||||
*/
|
||||
|
||||
int RoundDownToPowerOfTwo( int num ) {
|
||||
int pot;
|
||||
for (pot = 1 ; (pot*2) <= num ; pot<<=1) {
|
||||
}
|
||||
return pot;
|
||||
}
|
||||
|
||||
static union {
|
||||
int intVal;
|
||||
byte color[4];
|
||||
} fillColor;
|
||||
|
||||
static byte colors[8][4] = {
|
||||
{ 0, 0, 0, 255 },
|
||||
{ 255, 0, 0, 255 },
|
||||
{ 0, 255, 0, 255 },
|
||||
{ 255, 255, 0, 255 },
|
||||
{ 0, 0, 255, 255 },
|
||||
{ 255, 0, 255, 255 },
|
||||
{ 0, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255 }
|
||||
};
|
||||
|
||||
static void R_EmptyLevelImage( idImage *image ) {
|
||||
int c = MAX_LEVEL_WIDTH * MAX_LEVEL_WIDTH;
|
||||
byte *data = (byte *)_alloca( c*4 );
|
||||
|
||||
for ( int i = 0 ; i < c ; i++ ) {
|
||||
((int *)data)[i] = fillColor.intVal;
|
||||
}
|
||||
|
||||
// FIXME: this won't live past vid mode changes
|
||||
image->GenerateImage( data, MAX_LEVEL_WIDTH, MAX_LEVEL_WIDTH,
|
||||
TF_DEFAULT, false, TR_REPEAT, TD_HIGH_QUALITY );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
InitFromMegaFile
|
||||
====================
|
||||
*/
|
||||
bool idMegaTexture::InitFromMegaFile( const char *fileBase ) {
|
||||
idStr name = "megaTextures/";
|
||||
name += fileBase;
|
||||
name.StripFileExtension();
|
||||
name += ".mega";
|
||||
|
||||
int width, height;
|
||||
|
||||
fileHandle = fileSystem->OpenFileRead( name.c_str() );
|
||||
if ( !fileHandle ) {
|
||||
common->Printf( "idMegaTexture: failed to open %s\n", name.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
fileHandle->Read( &header, sizeof( header ) );
|
||||
if ( header.tileSize < 64 || header.tilesWide < 1 || header.tilesHigh < 1 ) {
|
||||
common->Printf( "idMegaTexture: bad header on %s\n", name.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
currentTriMapping = NULL;
|
||||
|
||||
numLevels = 0;
|
||||
width = header.tilesWide;
|
||||
height = header.tilesHigh;
|
||||
|
||||
int tileOffset = 1; // just past the header
|
||||
|
||||
memset( levels, 0, sizeof( levels ) );
|
||||
while( 1 ) {
|
||||
idTextureLevel *level = &levels[numLevels];
|
||||
|
||||
level->mega = this;
|
||||
level->tileOffset = tileOffset;
|
||||
level->tilesWide = width;
|
||||
level->tilesHigh = height;
|
||||
level->parms[0] = -1; // initially mask everything
|
||||
level->parms[1] = 0;
|
||||
level->parms[2] = 0;
|
||||
level->parms[3] = (float)width / TILE_PER_LEVEL;
|
||||
level->Invalidate();
|
||||
|
||||
tileOffset += level->tilesWide * level->tilesHigh;
|
||||
|
||||
char str[1024];
|
||||
sprintf( str, "MEGA_%s_%i", fileBase, numLevels );
|
||||
|
||||
// give each level a default fill color
|
||||
for (int i = 0 ; i < 4 ; i++ ) {
|
||||
fillColor.color[i] = colors[numLevels+1][i];
|
||||
}
|
||||
|
||||
levels[numLevels].image = globalImages->ImageFromFunction( str, R_EmptyLevelImage );
|
||||
numLevels++;
|
||||
|
||||
if ( width <= TILE_PER_LEVEL && height <= TILE_PER_LEVEL ) {
|
||||
break;
|
||||
}
|
||||
width = ( width + 1 ) >> 1;
|
||||
height = ( height + 1 ) >> 1;
|
||||
}
|
||||
|
||||
// force first bind to load everything
|
||||
currentViewOrigin[0] = -99999999.0f;
|
||||
currentViewOrigin[1] = -99999999.0f;
|
||||
currentViewOrigin[2] = -99999999.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
SetMappingForSurface
|
||||
|
||||
analyzes xyz and st to create a mapping
|
||||
This is not very robust, but works for rectangular grids
|
||||
====================
|
||||
*/
|
||||
void idMegaTexture::SetMappingForSurface( const srfTriangles_t *tri ) {
|
||||
if ( tri == currentTriMapping ) {
|
||||
return;
|
||||
}
|
||||
currentTriMapping = tri;
|
||||
|
||||
if ( !tri->verts ) {
|
||||
return;
|
||||
}
|
||||
|
||||
idDrawVert origin, axis[2];
|
||||
|
||||
origin.st[0] = 1.0;
|
||||
origin.st[1] = 1.0;
|
||||
|
||||
axis[0].st[0] = 0;
|
||||
axis[0].st[1] = 1;
|
||||
|
||||
axis[1].st[0] = 1;
|
||||
axis[1].st[1] = 0;
|
||||
|
||||
for ( int i = 0 ; i < tri->numVerts ; i++ ) {
|
||||
idDrawVert *v = &tri->verts[i];
|
||||
|
||||
if ( v->st[0] <= origin.st[0] && v->st[1] <= origin.st[1] ) {
|
||||
origin = *v;
|
||||
}
|
||||
if ( v->st[0] >= axis[0].st[0] && v->st[1] <= axis[0].st[1] ) {
|
||||
axis[0] = *v;
|
||||
}
|
||||
if ( v->st[0] <= axis[1].st[0] && v->st[1] >= axis[1].st[1] ) {
|
||||
axis[1] = *v;
|
||||
}
|
||||
}
|
||||
|
||||
for ( int i = 0 ; i < 2 ; i++ ) {
|
||||
idVec3 dir = axis[i].xyz - origin.xyz;
|
||||
float texLen = axis[i].st[i] - origin.st[i];
|
||||
float spaceLen = (axis[i].xyz - origin.xyz).Length();
|
||||
|
||||
float scale = texLen / (spaceLen*spaceLen);
|
||||
dir *= scale;
|
||||
|
||||
float c = origin.xyz * dir - origin.st[i];
|
||||
|
||||
localViewToTextureCenter[i][0] = dir[0];
|
||||
localViewToTextureCenter[i][1] = dir[1];
|
||||
localViewToTextureCenter[i][2] = dir[2];
|
||||
localViewToTextureCenter[i][3] = -c;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
BindForViewOrigin
|
||||
====================
|
||||
*/
|
||||
void idMegaTexture::BindForViewOrigin( const idVec3 viewOrigin ) {
|
||||
|
||||
SetViewOrigin( viewOrigin );
|
||||
|
||||
// borderClamp image goes in texture 0
|
||||
GL_SelectTexture( 0 );
|
||||
globalImages->borderClampImage->Bind();
|
||||
|
||||
// level images in higher textures, blurriest first
|
||||
for ( int i = 0 ; i < 7 ; i++ ) {
|
||||
GL_SelectTexture( 1+i );
|
||||
|
||||
if ( i >= numLevels ) {
|
||||
globalImages->whiteImage->Bind();
|
||||
|
||||
static float parms[4] = { -2, -2, 0, 1 }; // no contribution
|
||||
qglProgramLocalParameter4fvARB( GL_VERTEX_PROGRAM_ARB, i, parms );
|
||||
} else {
|
||||
idTextureLevel *level = &levels[ numLevels-1-i ];
|
||||
|
||||
if ( r_showMegaTexture.GetBool() ) {
|
||||
if ( i & 1 ) {
|
||||
globalImages->blackImage->Bind();
|
||||
} else {
|
||||
globalImages->whiteImage->Bind();
|
||||
}
|
||||
} else {
|
||||
level->image->Bind();
|
||||
}
|
||||
qglProgramLocalParameter4fvARB( GL_VERTEX_PROGRAM_ARB, i, level->parms );
|
||||
}
|
||||
}
|
||||
|
||||
float parms[4];
|
||||
parms[0] = 0;
|
||||
parms[1] = 0;
|
||||
parms[2] = 0;
|
||||
parms[3] = 1;
|
||||
qglProgramLocalParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 7, parms );
|
||||
|
||||
parms[0] = 1;
|
||||
parms[1] = 1;
|
||||
parms[2] = r_terrainScale.GetFloat();
|
||||
parms[3] = 1;
|
||||
qglProgramLocalParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 8, parms );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
Unbind
|
||||
|
||||
This can go away once everything uses fragment programs so the enable states don't
|
||||
need tracking
|
||||
====================
|
||||
*/
|
||||
void idMegaTexture::Unbind( void ) {
|
||||
for ( int i = 0 ; i < numLevels ; i++ ) {
|
||||
GL_SelectTexture( 1+i );
|
||||
globalImages->BindNull();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
SetViewOrigin
|
||||
====================
|
||||
*/
|
||||
void idMegaTexture::SetViewOrigin( const idVec3 viewOrigin ) {
|
||||
if ( r_showMegaTextureLabels.IsModified() ) {
|
||||
r_showMegaTextureLabels.ClearModified();
|
||||
currentViewOrigin[0] = viewOrigin[0] + 0.1; // force a change
|
||||
for ( int i = 0 ; i < numLevels ; i++ ) {
|
||||
levels[i].Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
if ( viewOrigin == currentViewOrigin ) {
|
||||
return;
|
||||
}
|
||||
if ( r_skipMegaTexture.GetBool() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentViewOrigin = viewOrigin;
|
||||
|
||||
float texCenter[2];
|
||||
|
||||
// convert the viewOrigin to a texture center, which will
|
||||
// be a different conversion for each megaTexture
|
||||
for ( int i = 0 ; i < 2 ; i++ ) {
|
||||
texCenter[i] =
|
||||
viewOrigin[0] * localViewToTextureCenter[i][0] +
|
||||
viewOrigin[1] * localViewToTextureCenter[i][1] +
|
||||
viewOrigin[2] * localViewToTextureCenter[i][2] +
|
||||
localViewToTextureCenter[i][3];
|
||||
}
|
||||
|
||||
for ( int i = 0 ; i < numLevels ; i++ ) {
|
||||
levels[i].UpdateForCenter( texCenter );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
UpdateTile
|
||||
|
||||
A local tile will only be mapped to globalTile[ localTile + X * TILE_PER_LEVEL ] for some x
|
||||
====================
|
||||
*/
|
||||
void idTextureLevel::UpdateTile( int localX, int localY, int globalX, int globalY ) {
|
||||
idTextureTile *tile = &tileMap[localX][localY];
|
||||
|
||||
if ( tile->x == globalX && tile->y == globalY ) {
|
||||
return;
|
||||
}
|
||||
if ( (globalX & (TILE_PER_LEVEL-1)) != localX || (globalY & (TILE_PER_LEVEL-1)) != localY ) {
|
||||
common->Error( "idTextureLevel::UpdateTile: bad coordinate mod" );
|
||||
}
|
||||
|
||||
tile->x = globalX;
|
||||
tile->y = globalY;
|
||||
|
||||
byte data[ TILE_SIZE * TILE_SIZE * 4 ];
|
||||
|
||||
if ( globalX >= tilesWide || globalX < 0 || globalY >= tilesHigh || globalY < 0 ) {
|
||||
// off the map
|
||||
memset( data, 0, sizeof( data ) );
|
||||
} else {
|
||||
// extract the data from the full image (FIXME: background load from disk)
|
||||
int tileNum = tileOffset + tile->y * tilesWide + tile->x;
|
||||
|
||||
int tileSize = TILE_SIZE * TILE_SIZE * 4;
|
||||
|
||||
mega->fileHandle->Seek( tileNum * tileSize, FS_SEEK_SET );
|
||||
memset( data, 128, sizeof( data ) );
|
||||
mega->fileHandle->Read( data, tileSize );
|
||||
}
|
||||
|
||||
if ( idMegaTexture::r_showMegaTextureLabels.GetBool() ) {
|
||||
// put a color marker in it
|
||||
byte color[4] = { 255 * localX / TILE_PER_LEVEL, 255 * localY / TILE_PER_LEVEL, 0, 0 };
|
||||
for ( int x = 0 ; x < 8 ; x++ ) {
|
||||
for ( int y = 0 ; y < 8 ; y++ ) {
|
||||
*(int *)&data[ ( ( y + TILE_SIZE/2 - 4 ) * TILE_SIZE + x + TILE_SIZE/2 - 4 ) * 4 ] = *(int *)color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// upload all the mip-map levels
|
||||
int level = 0;
|
||||
int size = TILE_SIZE;
|
||||
while ( 1 ) {
|
||||
qglTexSubImage2D( GL_TEXTURE_2D, level, localX * size, localY * size, size, size, GL_RGBA, GL_UNSIGNED_BYTE, data );
|
||||
size >>= 1;
|
||||
level++;
|
||||
|
||||
if ( size == 0 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
int byteSize = size * 4;
|
||||
// mip-map in place
|
||||
for ( int y = 0 ; y < size ; y++ ) {
|
||||
byte *in, *in2, *out;
|
||||
in = data + y * size * 16;
|
||||
in2 = in + size * 8;
|
||||
out = data + y * size * 4;
|
||||
for ( int x = 0 ; x < size ; x++ ) {
|
||||
out[x*4+0] = ( in[x*8+0] + in[x*8+4+0] + in2[x*8+0] + in2[x*8+4+0] ) >> 2;
|
||||
out[x*4+1] = ( in[x*8+1] + in[x*8+4+1] + in2[x*8+1] + in2[x*8+4+1] ) >> 2;
|
||||
out[x*4+2] = ( in[x*8+2] + in[x*8+4+2] + in2[x*8+2] + in2[x*8+4+2] ) >> 2;
|
||||
out[x*4+3] = ( in[x*8+3] + in[x*8+4+3] + in2[x*8+3] + in2[x*8+4+3] ) >> 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
UpdateForCenter
|
||||
|
||||
Center is in the 0.0 to 1.0 range
|
||||
====================
|
||||
*/
|
||||
void idTextureLevel::UpdateForCenter( float center[2] ) {
|
||||
int globalTileCorner[2];
|
||||
int localTileOffset[2];
|
||||
|
||||
if ( tilesWide <= TILE_PER_LEVEL && tilesHigh <= TILE_PER_LEVEL ) {
|
||||
globalTileCorner[0] = 0;
|
||||
globalTileCorner[1] = 0;
|
||||
localTileOffset[0] = 0;
|
||||
localTileOffset[1] = 0;
|
||||
// orient the mask so that it doesn't mask anything at all
|
||||
parms[0] = 0.25;
|
||||
parms[1] = 0.25;
|
||||
parms[3] = 0.25;
|
||||
} else {
|
||||
for ( int i = 0 ; i < 2 ; i++ ) {
|
||||
float global[2];
|
||||
|
||||
// this value will be outside the 0.0 to 1.0 range unless
|
||||
// we are in the corner of the megaTexture
|
||||
global[i] = ( center[i] * parms[3] - 0.5 ) * TILE_PER_LEVEL;
|
||||
|
||||
globalTileCorner[i] = (int)( global[i] + 0.5 );
|
||||
|
||||
localTileOffset[i] = globalTileCorner[i] & (TILE_PER_LEVEL-1);
|
||||
|
||||
// scaling for the mask texture to only allow the proper window
|
||||
// of tiles to show through
|
||||
parms[i] = -globalTileCorner[i] / (float)TILE_PER_LEVEL;
|
||||
}
|
||||
}
|
||||
|
||||
image->Bind();
|
||||
|
||||
for ( int x = 0 ; x < TILE_PER_LEVEL ; x++ ) {
|
||||
for ( int y = 0 ; y < TILE_PER_LEVEL ; y++ ) {
|
||||
int globalTile[2];
|
||||
|
||||
globalTile[0] = globalTileCorner[0] + ( ( x - localTileOffset[0] ) & (TILE_PER_LEVEL-1) );
|
||||
globalTile[1] = globalTileCorner[1] + ( ( y - localTileOffset[1] ) & (TILE_PER_LEVEL-1) );
|
||||
|
||||
UpdateTile( x, y, globalTile[0], globalTile[1] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
Invalidate
|
||||
|
||||
Forces all tiles to be regenerated
|
||||
=====================
|
||||
*/
|
||||
void idTextureLevel::Invalidate() {
|
||||
for ( int x = 0 ; x < TILE_PER_LEVEL ; x++ ) {
|
||||
for ( int y = 0 ; y < TILE_PER_LEVEL ; y++ ) {
|
||||
tileMap[x][y].x =
|
||||
tileMap[x][y].y = -99999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================================================
|
||||
|
||||
|
||||
typedef struct _TargaHeader {
|
||||
unsigned char id_length, colormap_type, image_type;
|
||||
unsigned short colormap_index, colormap_length;
|
||||
unsigned char colormap_size;
|
||||
unsigned short x_origin, y_origin, width, height;
|
||||
unsigned char pixel_size, attributes;
|
||||
} TargaHeader;
|
||||
|
||||
|
||||
static byte ReadByte( idFile *f ) {
|
||||
byte b;
|
||||
|
||||
f->Read( &b, 1 );
|
||||
return b;
|
||||
}
|
||||
|
||||
static short ReadShort( idFile *f ) {
|
||||
byte b[2];
|
||||
|
||||
f->Read( &b, 2 );
|
||||
|
||||
return b[0] + ( b[1] << 8 );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
GenerateMegaMipMaps
|
||||
====================
|
||||
*/
|
||||
void idMegaTexture::GenerateMegaMipMaps( megaTextureHeader_t *header, idFile *outFile ) {
|
||||
outFile->Flush();
|
||||
|
||||
// out fileSystem doesn't allow read / write access...
|
||||
idFile *inFile = fileSystem->OpenFileRead( outFile->GetName() );
|
||||
|
||||
int tileOffset = 1;
|
||||
int width = header->tilesWide;
|
||||
int height = header->tilesHigh;
|
||||
|
||||
int tileSize = header->tileSize * header->tileSize * 4;
|
||||
byte *oldBlock = (byte *)_alloca( tileSize );
|
||||
byte *newBlock = (byte *)_alloca( tileSize );
|
||||
|
||||
while ( width > 1 || height > 1 ) {
|
||||
int newHeight = (height+1) >> 1;
|
||||
if ( newHeight < 1 ) {
|
||||
newHeight = 1;
|
||||
}
|
||||
int newWidth = (width+1) >> 1;
|
||||
if ( width < 1 ) {
|
||||
width = 1;
|
||||
}
|
||||
common->Printf( "generating %i x %i block mip level\n", newWidth, newHeight );
|
||||
|
||||
int tileNum;
|
||||
|
||||
for ( int y = 0 ; y < newHeight ; y++ ) {
|
||||
common->Printf( "row %i\n", y );
|
||||
session->UpdateScreen();
|
||||
|
||||
for ( int x = 0 ; x < newWidth ; x++ ) {
|
||||
// mip map four original blocks down into a single new block
|
||||
for ( int yy = 0 ; yy < 2 ; yy++ ) {
|
||||
for ( int xx = 0 ; xx< 2 ; xx++ ) {
|
||||
int tx = x*2 + xx;
|
||||
int ty = y*2 + yy;
|
||||
|
||||
if ( tx > width || ty > height ) {
|
||||
// off edge, zero fill
|
||||
memset( newBlock, 0, sizeof( newBlock ) );
|
||||
} else {
|
||||
tileNum = tileOffset + ty * width + tx;
|
||||
inFile->Seek( tileNum * tileSize, FS_SEEK_SET );
|
||||
inFile->Read( oldBlock, tileSize );
|
||||
}
|
||||
// mip map the new pixels
|
||||
for ( int yyy = 0 ; yyy < TILE_SIZE / 2 ; yyy++ ) {
|
||||
for ( int xxx = 0 ; xxx < TILE_SIZE / 2 ; xxx++ ) {
|
||||
byte *in = &oldBlock[ ( yyy * 2 * TILE_SIZE + xxx * 2 ) * 4 ];
|
||||
byte *out = &newBlock[ ( ( ( TILE_SIZE/2 * yy ) + yyy ) * TILE_SIZE + ( TILE_SIZE/2 * xx ) + xxx ) * 4 ];
|
||||
out[0] = ( in[0] + in[4] + in[0+TILE_SIZE*4] + in[4+TILE_SIZE*4] ) >> 2;
|
||||
out[1] = ( in[1] + in[5] + in[1+TILE_SIZE*4] + in[5+TILE_SIZE*4] ) >> 2;
|
||||
out[2] = ( in[2] + in[6] + in[2+TILE_SIZE*4] + in[6+TILE_SIZE*4] ) >> 2;
|
||||
out[3] = ( in[3] + in[7] + in[3+TILE_SIZE*4] + in[7+TILE_SIZE*4] ) >> 2;
|
||||
}
|
||||
}
|
||||
|
||||
// write the block out
|
||||
tileNum = tileOffset + width * height + y * newWidth + x;
|
||||
outFile->Seek( tileNum * tileSize, FS_SEEK_SET );
|
||||
outFile->Write( newBlock, tileSize );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tileOffset += width * height;
|
||||
width = newWidth;
|
||||
height = newHeight;
|
||||
}
|
||||
|
||||
delete inFile;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
GenerateMegaPreview
|
||||
|
||||
Make a 2k x 2k preview image for a mega texture that can be used in modeling programs
|
||||
====================
|
||||
*/
|
||||
void idMegaTexture::GenerateMegaPreview( const char *fileName ) {
|
||||
idFile *fileHandle = fileSystem->OpenFileRead( fileName );
|
||||
if ( !fileHandle ) {
|
||||
common->Printf( "idMegaTexture: failed to open %s\n", fileName );
|
||||
return;
|
||||
}
|
||||
|
||||
idStr outName = fileName;
|
||||
outName.StripFileExtension();
|
||||
outName += "_preview.tga";
|
||||
|
||||
common->Printf( "Creating %s.\n", outName.c_str() );
|
||||
|
||||
megaTextureHeader_t header;
|
||||
|
||||
fileHandle->Read( &header, sizeof( header ) );
|
||||
if ( header.tileSize < 64 || header.tilesWide < 1 || header.tilesHigh < 1 ) {
|
||||
common->Printf( "idMegaTexture: bad header on %s\n", fileName );
|
||||
return;
|
||||
}
|
||||
|
||||
int tileSize = header.tileSize;
|
||||
int width = header.tilesWide;
|
||||
int height = header.tilesHigh;
|
||||
int tileOffset = 1;
|
||||
int tileBytes = tileSize * tileSize * 4;
|
||||
// find the level that fits
|
||||
while ( width * tileSize > 2048 || height * tileSize > 2048 ) {
|
||||
tileOffset += width * height;
|
||||
width >>= 1;
|
||||
if ( width < 1 ) {
|
||||
width = 1;
|
||||
}
|
||||
height >>= 1;
|
||||
if ( height < 1 ) {
|
||||
height = 1;
|
||||
}
|
||||
}
|
||||
|
||||
byte *pic = (byte *)R_StaticAlloc( width * height * tileBytes );
|
||||
byte *oldBlock = (byte *)_alloca( tileBytes );
|
||||
for ( int y = 0 ; y < height ; y++ ) {
|
||||
for ( int x = 0 ; x < width ; x++ ) {
|
||||
int tileNum = tileOffset + y * width + x;
|
||||
fileHandle->Seek( tileNum * tileBytes, FS_SEEK_SET );
|
||||
fileHandle->Read( oldBlock, tileBytes );
|
||||
|
||||
for ( int yy = 0 ; yy < tileSize ; yy++ ) {
|
||||
memcpy( pic + ( ( y * tileSize + yy ) * width * tileSize + x * tileSize ) * 4,
|
||||
oldBlock + yy * tileSize * 4, tileSize * 4 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
R_WriteTGA( outName.c_str(), pic, width * tileSize, height * tileSize, false );
|
||||
|
||||
R_StaticFree( pic );
|
||||
|
||||
delete fileHandle;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
MakeMegaTexture_f
|
||||
|
||||
Incrementally load a giant tga file and process into the mega texture block format
|
||||
====================
|
||||
*/
|
||||
void idMegaTexture::MakeMegaTexture_f( const idCmdArgs &args ) {
|
||||
int columns, rows, fileSize, numBytes;
|
||||
byte *pixbuf;
|
||||
int row, column;
|
||||
TargaHeader targa_header;
|
||||
|
||||
if ( args.Argc() != 2 ) {
|
||||
common->Printf( "USAGE: makeMegaTexture <filebase>\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
idStr name_s = "megaTextures/";
|
||||
name_s += args.Argv(1);
|
||||
name_s.StripFileExtension();
|
||||
name_s += ".tga";
|
||||
|
||||
const char *name = name_s.c_str();
|
||||
|
||||
//
|
||||
// open the file
|
||||
//
|
||||
common->Printf( "Opening %s.\n", name );
|
||||
fileSize = fileSystem->ReadFile( name, NULL, NULL );
|
||||
idFile *file = fileSystem->OpenFileRead( name );
|
||||
|
||||
if ( !file ) {
|
||||
common->Printf( "Couldn't open %s\n", name );
|
||||
return;
|
||||
}
|
||||
|
||||
targa_header.id_length = ReadByte( file );
|
||||
targa_header.colormap_type = ReadByte( file );
|
||||
targa_header.image_type = ReadByte( file );
|
||||
|
||||
targa_header.colormap_index = ReadShort( file );
|
||||
targa_header.colormap_length = ReadShort( file );
|
||||
targa_header.colormap_size = ReadByte( file );
|
||||
targa_header.x_origin = ReadShort( file );
|
||||
targa_header.y_origin = ReadShort( file );
|
||||
targa_header.width = ReadShort( file );
|
||||
targa_header.height = ReadShort( file );
|
||||
targa_header.pixel_size = ReadByte( file );
|
||||
targa_header.attributes = ReadByte( file );
|
||||
|
||||
if ( targa_header.image_type != 2 && targa_header.image_type != 10 && targa_header.image_type != 3 ) {
|
||||
common->Error( "LoadTGA( %s ): Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n", name );
|
||||
}
|
||||
|
||||
if ( targa_header.colormap_type != 0 ) {
|
||||
common->Error( "LoadTGA( %s ): colormaps not supported\n", name );
|
||||
}
|
||||
|
||||
if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 ) {
|
||||
common->Error( "LoadTGA( %s ): Only 32 or 24 bit images supported (no colormaps)\n", name );
|
||||
}
|
||||
|
||||
if ( targa_header.image_type == 2 || targa_header.image_type == 3 ) {
|
||||
numBytes = targa_header.width * targa_header.height * ( targa_header.pixel_size >> 3 );
|
||||
if ( numBytes > fileSize - 18 - targa_header.id_length ) {
|
||||
common->Error( "LoadTGA( %s ): incomplete file\n", name );
|
||||
}
|
||||
}
|
||||
|
||||
columns = targa_header.width;
|
||||
rows = targa_header.height;
|
||||
|
||||
// skip TARGA image comment
|
||||
if ( targa_header.id_length != 0 ) {
|
||||
file->Seek( targa_header.id_length, FS_SEEK_CUR );
|
||||
}
|
||||
|
||||
megaTextureHeader_t mtHeader;
|
||||
|
||||
mtHeader.tileSize = TILE_SIZE;
|
||||
mtHeader.tilesWide = RoundDownToPowerOfTwo( targa_header.width ) / TILE_SIZE;
|
||||
mtHeader.tilesHigh = RoundDownToPowerOfTwo( targa_header.height ) / TILE_SIZE;
|
||||
|
||||
idStr outName = name;
|
||||
outName.StripFileExtension();
|
||||
outName += ".mega";
|
||||
|
||||
common->Printf( "Writing %i x %i size %i tiles to %s.\n",
|
||||
mtHeader.tilesWide, mtHeader.tilesHigh, mtHeader.tileSize, outName.c_str() );
|
||||
|
||||
// open the output megatexture file
|
||||
idFile *out = fileSystem->OpenFileWrite( outName.c_str() );
|
||||
|
||||
out->Write( &mtHeader, sizeof( mtHeader ) );
|
||||
out->Seek( TILE_SIZE * TILE_SIZE * 4, FS_SEEK_SET );
|
||||
|
||||
// we will process this one row of tiles at a time, since the entire thing
|
||||
// won't fit in memory
|
||||
byte *targa_rgba = (byte *)R_StaticAlloc( TILE_SIZE * targa_header.width * 4 );
|
||||
|
||||
int blockRowsRemaining = mtHeader.tilesHigh;
|
||||
while ( blockRowsRemaining-- ) {
|
||||
common->Printf( "%i blockRowsRemaining\n", blockRowsRemaining );
|
||||
session->UpdateScreen();
|
||||
|
||||
if ( targa_header.image_type == 2 || targa_header.image_type == 3 ) {
|
||||
// Uncompressed RGB or gray scale image
|
||||
for( row = 0 ; row < TILE_SIZE ; row++ ) {
|
||||
pixbuf = targa_rgba + row*columns*4;
|
||||
for( column = 0; column < columns; column++) {
|
||||
unsigned char red,green,blue,alphabyte;
|
||||
switch( targa_header.pixel_size ) {
|
||||
case 8:
|
||||
blue = ReadByte( file );
|
||||
green = blue;
|
||||
red = blue;
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = 255;
|
||||
break;
|
||||
|
||||
case 24:
|
||||
blue = ReadByte( file );
|
||||
green = ReadByte( file );
|
||||
red = ReadByte( file );
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = 255;
|
||||
break;
|
||||
case 32:
|
||||
blue = ReadByte( file );
|
||||
green = ReadByte( file );
|
||||
red = ReadByte( file );
|
||||
alphabyte = ReadByte( file );
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = alphabyte;
|
||||
break;
|
||||
default:
|
||||
common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ( targa_header.image_type == 10 ) { // Runlength encoded RGB images
|
||||
unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
|
||||
|
||||
red = 0;
|
||||
green = 0;
|
||||
blue = 0;
|
||||
alphabyte = 0xff;
|
||||
|
||||
for( row = 0 ; row < TILE_SIZE ; row++ ) {
|
||||
pixbuf = targa_rgba + row*columns*4;
|
||||
for( column = 0; column < columns; ) {
|
||||
packetHeader= ReadByte( file );
|
||||
packetSize = 1 + (packetHeader & 0x7f);
|
||||
if ( packetHeader & 0x80 ) { // run-length packet
|
||||
switch( targa_header.pixel_size ) {
|
||||
case 24:
|
||||
blue = ReadByte( file );
|
||||
green = ReadByte( file );
|
||||
red = ReadByte( file );
|
||||
alphabyte = 255;
|
||||
break;
|
||||
case 32:
|
||||
blue = ReadByte( file );
|
||||
green = ReadByte( file );
|
||||
red = ReadByte( file );
|
||||
alphabyte = ReadByte( file );
|
||||
break;
|
||||
default:
|
||||
common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
|
||||
break;
|
||||
}
|
||||
|
||||
for( j = 0; j < packetSize; j++ ) {
|
||||
*pixbuf++=red;
|
||||
*pixbuf++=green;
|
||||
*pixbuf++=blue;
|
||||
*pixbuf++=alphabyte;
|
||||
column++;
|
||||
if ( column == columns ) { // run spans across rows
|
||||
common->Error( "TGA had RLE across columns, probably breaks block" );
|
||||
column = 0;
|
||||
if ( row > 0) {
|
||||
row--;
|
||||
}
|
||||
else {
|
||||
goto breakOut;
|
||||
}
|
||||
pixbuf = targa_rgba + row*columns*4;
|
||||
}
|
||||
}
|
||||
} else { // non run-length packet
|
||||
for( j = 0; j < packetSize; j++ ) {
|
||||
switch( targa_header.pixel_size ) {
|
||||
case 24:
|
||||
blue = ReadByte( file );
|
||||
green = ReadByte( file );
|
||||
red = ReadByte( file );
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = 255;
|
||||
break;
|
||||
case 32:
|
||||
blue = ReadByte( file );
|
||||
green = ReadByte( file );
|
||||
red = ReadByte( file );
|
||||
alphabyte = ReadByte( file );
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = alphabyte;
|
||||
break;
|
||||
default:
|
||||
common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
|
||||
break;
|
||||
}
|
||||
column++;
|
||||
if ( column == columns ) { // pixel packet run spans across rows
|
||||
column = 0;
|
||||
if ( row > 0 ) {
|
||||
row--;
|
||||
}
|
||||
else {
|
||||
goto breakOut;
|
||||
}
|
||||
pixbuf = targa_rgba + row*columns*4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
breakOut: ;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// write out individual blocks from the full row block buffer
|
||||
//
|
||||
for ( int rowBlock = 0 ; rowBlock < mtHeader.tilesWide ; rowBlock++ ) {
|
||||
for ( int y = 0 ; y < TILE_SIZE ; y++ ) {
|
||||
out->Write( targa_rgba + ( y * targa_header.width + rowBlock * TILE_SIZE ) * 4, TILE_SIZE * 4 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
R_StaticFree( targa_rgba );
|
||||
|
||||
GenerateMegaMipMaps( &mtHeader, out );
|
||||
|
||||
delete out;
|
||||
delete file;
|
||||
|
||||
GenerateMegaPreview( outName.c_str() );
|
||||
#if 0
|
||||
if ( (targa_header.attributes & (1<<5)) ) { // image flp bit
|
||||
R_VerticalFlip( *pic, *width, *height );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
99
neo/renderer/MegaTexture.h
Normal file
99
neo/renderer/MegaTexture.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
class idTextureTile {
|
||||
public:
|
||||
int x, y;
|
||||
};
|
||||
|
||||
static const int TILE_PER_LEVEL = 4;
|
||||
static const int MAX_MEGA_CHANNELS = 3; // normal, diffuse, specular
|
||||
static const int MAX_LEVELS = 12;
|
||||
static const int MAX_LEVEL_WIDTH = 512;
|
||||
static const int TILE_SIZE = MAX_LEVEL_WIDTH / TILE_PER_LEVEL;
|
||||
|
||||
class idMegaTexture;
|
||||
|
||||
class idTextureLevel {
|
||||
public:
|
||||
idMegaTexture *mega;
|
||||
|
||||
int tileOffset;
|
||||
int tilesWide;
|
||||
int tilesHigh;
|
||||
|
||||
idImage *image;
|
||||
idTextureTile tileMap[TILE_PER_LEVEL][TILE_PER_LEVEL];
|
||||
|
||||
float parms[4];
|
||||
|
||||
void UpdateForCenter( float center[2] );
|
||||
void UpdateTile( int localX, int localY, int globalX, int globalY );
|
||||
void Invalidate();
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int tileSize;
|
||||
int tilesWide;
|
||||
int tilesHigh;
|
||||
} megaTextureHeader_t;
|
||||
|
||||
|
||||
class idMegaTexture {
|
||||
public:
|
||||
bool InitFromMegaFile( const char *fileBase );
|
||||
void SetMappingForSurface( const srfTriangles_t *tri ); // analyzes xyz and st to create a mapping
|
||||
void BindForViewOrigin( const idVec3 origin ); // binds images and sets program parameters
|
||||
void Unbind(); // removes texture bindings
|
||||
|
||||
static void MakeMegaTexture_f( const idCmdArgs &args );
|
||||
private:
|
||||
friend class idTextureLevel;
|
||||
void SetViewOrigin( const idVec3 origin );
|
||||
static void GenerateMegaMipMaps( megaTextureHeader_t *header, idFile *file );
|
||||
static void GenerateMegaPreview( const char *fileName );
|
||||
|
||||
idFile *fileHandle;
|
||||
|
||||
const srfTriangles_t *currentTriMapping;
|
||||
|
||||
idVec3 currentViewOrigin;
|
||||
|
||||
float localViewToTextureCenter[2][4];
|
||||
|
||||
int numLevels;
|
||||
idTextureLevel levels[MAX_LEVELS]; // 0 is the highest resolution
|
||||
megaTextureHeader_t header;
|
||||
|
||||
static idCVar r_megaTextureLevel;
|
||||
static idCVar r_showMegaTexture;
|
||||
static idCVar r_showMegaTextureLabels;
|
||||
static idCVar r_skipMegaTexture;
|
||||
static idCVar r_terrainScale;
|
||||
};
|
||||
|
||||
2328
neo/renderer/Model.cpp
Normal file
2328
neo/renderer/Model.cpp
Normal file
File diff suppressed because it is too large
Load Diff
314
neo/renderer/Model.h
Normal file
314
neo/renderer/Model.h
Normal file
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __MODEL_H__
|
||||
#define __MODEL_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Render Model
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
// shared between the renderer, game, and Maya export DLL
|
||||
#define MD5_VERSION_STRING "MD5Version"
|
||||
#define MD5_MESH_EXT "md5mesh"
|
||||
#define MD5_ANIM_EXT "md5anim"
|
||||
#define MD5_CAMERA_EXT "md5camera"
|
||||
#define MD5_VERSION 10
|
||||
|
||||
// using shorts for triangle indexes can save a significant amount of traffic, but
|
||||
// to support the large models that renderBump loads, they need to be 32 bits
|
||||
#if 1
|
||||
|
||||
#define GL_INDEX_TYPE GL_UNSIGNED_INT
|
||||
typedef int glIndex_t;
|
||||
|
||||
#else
|
||||
|
||||
#define GL_INDEX_TYPE GL_UNSIGNED_SHORT
|
||||
typedef short glIndex_t;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
// NOTE: making this a glIndex is dubious, as there can be 2x the faces as verts
|
||||
glIndex_t p1, p2; // planes defining the edge
|
||||
glIndex_t v1, v2; // verts defining the edge
|
||||
} silEdge_t;
|
||||
|
||||
// this is used for calculating unsmoothed normals and tangents for deformed models
|
||||
typedef struct dominantTri_s {
|
||||
glIndex_t v2, v3;
|
||||
float normalizationScale[3];
|
||||
} dominantTri_t;
|
||||
|
||||
typedef struct lightingCache_s {
|
||||
idVec3 localLightVector; // this is the statically computed vector to the light
|
||||
// in texture space for cards without vertex programs
|
||||
} lightingCache_t;
|
||||
|
||||
typedef struct shadowCache_s {
|
||||
idVec4 xyz; // we use homogenous coordinate tricks
|
||||
} shadowCache_t;
|
||||
|
||||
const int SHADOW_CAP_INFINITE = 64;
|
||||
|
||||
// our only drawing geometry type
|
||||
typedef struct srfTriangles_s {
|
||||
idBounds bounds; // for culling
|
||||
|
||||
int ambientViewCount; // if == tr.viewCount, it is visible this view
|
||||
|
||||
bool generateNormals; // create normals from geometry, instead of using explicit ones
|
||||
bool tangentsCalculated; // set when the vertex tangents have been calculated
|
||||
bool facePlanesCalculated; // set when the face planes have been calculated
|
||||
bool perfectHull; // true if there aren't any dangling edges
|
||||
bool deformedSurface; // if true, indexes, silIndexes, mirrorVerts, and silEdges are
|
||||
// pointers into the original surface, and should not be freed
|
||||
|
||||
int numVerts; // number of vertices
|
||||
idDrawVert * verts; // vertices, allocated with special allocator
|
||||
|
||||
int numIndexes; // for shadows, this has both front and rear end caps and silhouette planes
|
||||
glIndex_t * indexes; // indexes, allocated with special allocator
|
||||
|
||||
glIndex_t * silIndexes; // indexes changed to be the first vertex with same XYZ, ignoring normal and texcoords
|
||||
|
||||
int numMirroredVerts; // this many verts at the end of the vert list are tangent mirrors
|
||||
int * mirroredVerts; // tri->mirroredVerts[0] is the mirror of tri->numVerts - tri->numMirroredVerts + 0
|
||||
|
||||
int numDupVerts; // number of duplicate vertexes
|
||||
int * dupVerts; // pairs of the number of the first vertex and the number of the duplicate vertex
|
||||
|
||||
int numSilEdges; // number of silhouette edges
|
||||
silEdge_t * silEdges; // silhouette edges
|
||||
|
||||
idPlane * facePlanes; // [numIndexes/3] plane equations
|
||||
|
||||
dominantTri_t * dominantTris; // [numVerts] for deformed surface fast tangent calculation
|
||||
|
||||
int numShadowIndexesNoFrontCaps; // shadow volumes with front caps omitted
|
||||
int numShadowIndexesNoCaps; // shadow volumes with the front and rear caps omitted
|
||||
|
||||
int shadowCapPlaneBits; // bits 0-5 are set when that plane of the interacting light has triangles
|
||||
// projected on it, which means that if the view is on the outside of that
|
||||
// plane, we need to draw the rear caps of the shadow volume
|
||||
// turboShadows will have SHADOW_CAP_INFINITE
|
||||
|
||||
shadowCache_t * shadowVertexes; // these will be copied to shadowCache when it is going to be drawn.
|
||||
// these are NULL when vertex programs are available
|
||||
|
||||
struct srfTriangles_s * ambientSurface; // for light interactions, point back at the original surface that generated
|
||||
// the interaction, which we will get the ambientCache from
|
||||
|
||||
struct srfTriangles_s * nextDeferredFree; // chain of tris to free next frame
|
||||
|
||||
// data in vertex object space, not directly readable by the CPU
|
||||
struct vertCache_s * indexCache; // int
|
||||
struct vertCache_s * ambientCache; // idDrawVert
|
||||
struct vertCache_s * lightingCache; // lightingCache_t
|
||||
struct vertCache_s * shadowCache; // shadowCache_t
|
||||
} srfTriangles_t;
|
||||
|
||||
typedef idList<srfTriangles_t *> idTriList;
|
||||
|
||||
typedef struct modelSurface_s {
|
||||
int id;
|
||||
const idMaterial * shader;
|
||||
srfTriangles_t * geometry;
|
||||
} modelSurface_t;
|
||||
|
||||
typedef enum {
|
||||
DM_STATIC, // never creates a dynamic model
|
||||
DM_CACHED, // once created, stays constant until the entity is updated (animating characters)
|
||||
DM_CONTINUOUS // must be recreated for every single view (time dependent things like particles)
|
||||
} dynamicModel_t;
|
||||
|
||||
typedef enum {
|
||||
INVALID_JOINT = -1
|
||||
} jointHandle_t;
|
||||
|
||||
class idMD5Joint {
|
||||
public:
|
||||
idMD5Joint() { parent = NULL; }
|
||||
idStr name;
|
||||
const idMD5Joint * parent;
|
||||
};
|
||||
|
||||
|
||||
// the init methods may be called again on an already created model when
|
||||
// a reloadModels is issued
|
||||
|
||||
class idRenderModel {
|
||||
public:
|
||||
virtual ~idRenderModel() {};
|
||||
|
||||
// Loads static models only, dynamic models must be loaded by the modelManager
|
||||
virtual void InitFromFile( const char *fileName ) = 0;
|
||||
|
||||
// renderBump uses this to load the very high poly count models, skipping the
|
||||
// shadow and tangent generation, along with some surface cleanup to make it load faster
|
||||
virtual void PartialInitFromFile( const char *fileName ) = 0;
|
||||
|
||||
// this is used for dynamically created surfaces, which are assumed to not be reloadable.
|
||||
// It can be called again to clear out the surfaces of a dynamic model for regeneration.
|
||||
virtual void InitEmpty( const char *name ) = 0;
|
||||
|
||||
// dynamic model instantiations will be created with this
|
||||
// the geometry data will be owned by the model, and freed when it is freed
|
||||
// the geoemtry should be raw triangles, with no extra processing
|
||||
virtual void AddSurface( modelSurface_t surface ) = 0;
|
||||
|
||||
// cleans all the geometry and performs cross-surface processing
|
||||
// like shadow hulls
|
||||
// Creates the duplicated back side geometry for two sided, alpha tested, lit materials
|
||||
// This does not need to be called if none of the surfaces added with AddSurface require
|
||||
// light interaction, and all the triangles are already well formed.
|
||||
virtual void FinishSurfaces() = 0;
|
||||
|
||||
// frees all the data, but leaves the class around for dangling references,
|
||||
// which can regenerate the data with LoadModel()
|
||||
virtual void PurgeModel() = 0;
|
||||
|
||||
// resets any model information that needs to be reset on a same level load etc..
|
||||
// currently only implemented for liquids
|
||||
virtual void Reset() = 0;
|
||||
|
||||
// used for initial loads, reloadModel, and reloading the data of purged models
|
||||
// Upon exit, the model will absolutely be valid, but possibly as a default model
|
||||
virtual void LoadModel() = 0;
|
||||
|
||||
// internal use
|
||||
virtual bool IsLoaded() = 0;
|
||||
virtual void SetLevelLoadReferenced( bool referenced ) = 0;
|
||||
virtual bool IsLevelLoadReferenced() = 0;
|
||||
|
||||
// models that are already loaded at level start time
|
||||
// will still touch their data to make sure they
|
||||
// are kept loaded
|
||||
virtual void TouchData() = 0;
|
||||
|
||||
// dump any ambient caches on the model surfaces
|
||||
virtual void FreeVertexCache() = 0;
|
||||
|
||||
// returns the name of the model
|
||||
virtual const char * Name() const = 0;
|
||||
|
||||
// prints a detailed report on the model for printModel
|
||||
virtual void Print() const = 0;
|
||||
|
||||
// prints a single line report for listModels
|
||||
virtual void List() const = 0;
|
||||
|
||||
// reports the amount of memory (roughly) consumed by the model
|
||||
virtual int Memory() const = 0;
|
||||
|
||||
// for reloadModels
|
||||
virtual ID_TIME_T Timestamp() const = 0;
|
||||
|
||||
// returns the number of surfaces
|
||||
virtual int NumSurfaces() const = 0;
|
||||
|
||||
// NumBaseSurfaces will not count any overlays added to dynamic models
|
||||
virtual int NumBaseSurfaces() const = 0;
|
||||
|
||||
// get a pointer to a surface
|
||||
virtual const modelSurface_t *Surface( int surfaceNum ) const = 0;
|
||||
|
||||
// Allocates surface triangles.
|
||||
// Allocates memory for srfTriangles_t::verts and srfTriangles_t::indexes
|
||||
// The allocated memory is not initialized.
|
||||
// srfTriangles_t::numVerts and srfTriangles_t::numIndexes are set to zero.
|
||||
virtual srfTriangles_t * AllocSurfaceTriangles( int numVerts, int numIndexes ) const = 0;
|
||||
|
||||
// Frees surfaces triangles.
|
||||
virtual void FreeSurfaceTriangles( srfTriangles_t *tris ) const = 0;
|
||||
|
||||
// created at load time by stitching together all surfaces and sharing
|
||||
// the maximum number of edges. This may be incorrect if a skin file
|
||||
// remaps surfaces between shadow casting and non-shadow casting, or
|
||||
// if some surfaces are noSelfShadow and others aren't
|
||||
virtual srfTriangles_t * ShadowHull() const = 0;
|
||||
|
||||
// models of the form "_area*" may have a prelight shadow model associated with it
|
||||
virtual bool IsStaticWorldModel() const = 0;
|
||||
|
||||
// models parsed from inside map files or dynamically created cannot be reloaded by
|
||||
// reloadmodels
|
||||
virtual bool IsReloadable() const = 0;
|
||||
|
||||
// md3, md5, particles, etc
|
||||
virtual dynamicModel_t IsDynamicModel() const = 0;
|
||||
|
||||
// if the load failed for any reason, this will return true
|
||||
virtual bool IsDefaultModel() const = 0;
|
||||
|
||||
// dynamic models should return a fast, conservative approximation
|
||||
// static models should usually return the exact value
|
||||
virtual idBounds Bounds( const struct renderEntity_s *ent = NULL ) const = 0;
|
||||
|
||||
// returns value != 0.0f if the model requires the depth hack
|
||||
virtual float DepthHack() const = 0;
|
||||
|
||||
// returns a static model based on the definition and view
|
||||
// currently, this will be regenerated for every view, even though
|
||||
// some models, like character meshes, could be used for multiple (mirror)
|
||||
// views in a frame, or may stay static for multiple frames (corpses)
|
||||
// The renderer will delete the returned dynamic model the next view
|
||||
// This isn't const, because it may need to reload a purged model if it
|
||||
// wasn't precached correctly.
|
||||
virtual idRenderModel * InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) = 0;
|
||||
|
||||
// Returns the number of joints or 0 if the model is not an MD5
|
||||
virtual int NumJoints( void ) const = 0;
|
||||
|
||||
// Returns the MD5 joints or NULL if the model is not an MD5
|
||||
virtual const idMD5Joint * GetJoints( void ) const = 0;
|
||||
|
||||
// Returns the handle for the joint with the given name.
|
||||
virtual jointHandle_t GetJointHandle( const char *name ) const = 0;
|
||||
|
||||
// Returns the name for the joint with the given handle.
|
||||
virtual const char * GetJointName( jointHandle_t handle ) const = 0;
|
||||
|
||||
// Returns the default animation pose or NULL if the model is not an MD5.
|
||||
virtual const idJointQuat * GetDefaultPose( void ) const = 0;
|
||||
|
||||
// Returns number of the joint nearest to the given triangle.
|
||||
virtual int NearestJoint( int surfaceNum, int a, int c, int b ) const = 0;
|
||||
|
||||
// Writing to and reading from a demo file.
|
||||
virtual void ReadFromDemoFile( class idDemoFile *f ) = 0;
|
||||
virtual void WriteToDemoFile( class idDemoFile *f ) = 0;
|
||||
};
|
||||
|
||||
#endif /* !__MODEL_H__ */
|
||||
537
neo/renderer/ModelDecal.cpp
Normal file
537
neo/renderer/ModelDecal.cpp
Normal file
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
#include "Model_local.h"
|
||||
|
||||
// decalFade filter 5 0.1
|
||||
// polygonOffset
|
||||
// {
|
||||
// map invertColor( textures/splat )
|
||||
// blend GL_ZERO GL_ONE_MINUS_SRC
|
||||
// vertexColor
|
||||
// clamp
|
||||
// }
|
||||
|
||||
/*
|
||||
==================
|
||||
idRenderModelDecal::idRenderModelDecal
|
||||
==================
|
||||
*/
|
||||
idRenderModelDecal::idRenderModelDecal( void ) {
|
||||
memset( &tri, 0, sizeof( tri ) );
|
||||
tri.verts = verts;
|
||||
tri.indexes = indexes;
|
||||
material = NULL;
|
||||
nextDecal = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idRenderModelDecal::~idRenderModelDecal
|
||||
==================
|
||||
*/
|
||||
idRenderModelDecal::~idRenderModelDecal( void ) {
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idRenderModelDecal::idRenderModelDecal
|
||||
==================
|
||||
*/
|
||||
idRenderModelDecal *idRenderModelDecal::Alloc( void ) {
|
||||
return new idRenderModelDecal;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idRenderModelDecal::idRenderModelDecal
|
||||
==================
|
||||
*/
|
||||
void idRenderModelDecal::Free( idRenderModelDecal *decal ) {
|
||||
delete decal;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelDecal::CreateProjectionInfo
|
||||
=================
|
||||
*/
|
||||
bool idRenderModelDecal::CreateProjectionInfo( decalProjectionInfo_t &info, const idFixedWinding &winding, const idVec3 &projectionOrigin, const bool parallel, const float fadeDepth, const idMaterial *material, const int startTime ) {
|
||||
|
||||
if ( winding.GetNumPoints() != NUM_DECAL_BOUNDING_PLANES - 2 ) {
|
||||
common->Printf( "idRenderModelDecal::CreateProjectionInfo: winding must have %d points\n", NUM_DECAL_BOUNDING_PLANES - 2 );
|
||||
return false;
|
||||
}
|
||||
|
||||
assert( material != NULL );
|
||||
|
||||
info.projectionOrigin = projectionOrigin;
|
||||
info.material = material;
|
||||
info.parallel = parallel;
|
||||
info.fadeDepth = fadeDepth;
|
||||
info.startTime = startTime;
|
||||
info.force = false;
|
||||
|
||||
// get the winding plane and the depth of the projection volume
|
||||
idPlane windingPlane;
|
||||
winding.GetPlane( windingPlane );
|
||||
float depth = windingPlane.Distance( projectionOrigin );
|
||||
|
||||
// find the bounds for the projection
|
||||
winding.GetBounds( info.projectionBounds );
|
||||
if ( parallel ) {
|
||||
info.projectionBounds.ExpandSelf( depth );
|
||||
} else {
|
||||
info.projectionBounds.AddPoint( projectionOrigin );
|
||||
}
|
||||
|
||||
// calculate the world space projection volume bounding planes, positive sides face outside the decal
|
||||
if ( parallel ) {
|
||||
for ( int i = 0; i < winding.GetNumPoints(); i++ ) {
|
||||
idVec3 edge = winding[(i+1)%winding.GetNumPoints()].ToVec3() - winding[i].ToVec3();
|
||||
info.boundingPlanes[i].Normal().Cross( windingPlane.Normal(), edge );
|
||||
info.boundingPlanes[i].Normalize();
|
||||
info.boundingPlanes[i].FitThroughPoint( winding[i].ToVec3() );
|
||||
}
|
||||
} else {
|
||||
for ( int i = 0; i < winding.GetNumPoints(); i++ ) {
|
||||
info.boundingPlanes[i].FromPoints( projectionOrigin, winding[i].ToVec3(), winding[(i+1)%winding.GetNumPoints()].ToVec3() );
|
||||
}
|
||||
}
|
||||
info.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 2] = windingPlane;
|
||||
info.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 2][3] -= depth;
|
||||
info.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 1] = -windingPlane;
|
||||
|
||||
// fades will be from these plane
|
||||
info.fadePlanes[0] = windingPlane;
|
||||
info.fadePlanes[0][3] -= fadeDepth;
|
||||
info.fadePlanes[1] = -windingPlane;
|
||||
info.fadePlanes[1][3] += depth - fadeDepth;
|
||||
|
||||
// calculate the texture vectors for the winding
|
||||
float len, texArea, inva;
|
||||
idVec3 temp;
|
||||
idVec5 d0, d1;
|
||||
|
||||
const idVec5 &a = winding[0];
|
||||
const idVec5 &b = winding[1];
|
||||
const idVec5 &c = winding[2];
|
||||
|
||||
d0 = b.ToVec3() - a.ToVec3();
|
||||
d0.s = b.s - a.s;
|
||||
d0.t = b.t - a.t;
|
||||
d1 = c.ToVec3() - a.ToVec3();
|
||||
d1.s = c.s - a.s;
|
||||
d1.t = c.t - a.t;
|
||||
|
||||
texArea = ( d0[3] * d1[4] ) - ( d0[4] * d1[3] );
|
||||
inva = 1.0f / texArea;
|
||||
|
||||
temp[0] = ( d0[0] * d1[4] - d0[4] * d1[0] ) * inva;
|
||||
temp[1] = ( d0[1] * d1[4] - d0[4] * d1[1] ) * inva;
|
||||
temp[2] = ( d0[2] * d1[4] - d0[4] * d1[2] ) * inva;
|
||||
len = temp.Normalize();
|
||||
info.textureAxis[0].Normal() = temp * ( 1.0f / len );
|
||||
info.textureAxis[0][3] = winding[0].s - ( winding[0].ToVec3() * info.textureAxis[0].Normal() );
|
||||
|
||||
temp[0] = ( d0[3] * d1[0] - d0[0] * d1[3] ) * inva;
|
||||
temp[1] = ( d0[3] * d1[1] - d0[1] * d1[3] ) * inva;
|
||||
temp[2] = ( d0[3] * d1[2] - d0[2] * d1[3] ) * inva;
|
||||
len = temp.Normalize();
|
||||
info.textureAxis[1].Normal() = temp * ( 1.0f / len );
|
||||
info.textureAxis[1][3] = winding[0].t - ( winding[0].ToVec3() * info.textureAxis[1].Normal() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelDecal::CreateProjectionInfo
|
||||
=================
|
||||
*/
|
||||
void idRenderModelDecal::GlobalProjectionInfoToLocal( decalProjectionInfo_t &localInfo, const decalProjectionInfo_t &info, const idVec3 &origin, const idMat3 &axis ) {
|
||||
float modelMatrix[16];
|
||||
|
||||
R_AxisToModelMatrix( axis, origin, modelMatrix );
|
||||
|
||||
for ( int j = 0; j < NUM_DECAL_BOUNDING_PLANES; j++ ) {
|
||||
R_GlobalPlaneToLocal( modelMatrix, info.boundingPlanes[j], localInfo.boundingPlanes[j] );
|
||||
}
|
||||
R_GlobalPlaneToLocal( modelMatrix, info.fadePlanes[0], localInfo.fadePlanes[0] );
|
||||
R_GlobalPlaneToLocal( modelMatrix, info.fadePlanes[1], localInfo.fadePlanes[1] );
|
||||
R_GlobalPlaneToLocal( modelMatrix, info.textureAxis[0], localInfo.textureAxis[0] );
|
||||
R_GlobalPlaneToLocal( modelMatrix, info.textureAxis[1], localInfo.textureAxis[1] );
|
||||
R_GlobalPointToLocal( modelMatrix, info.projectionOrigin, localInfo.projectionOrigin );
|
||||
localInfo.projectionBounds = info.projectionBounds;
|
||||
localInfo.projectionBounds.TranslateSelf( -origin );
|
||||
localInfo.projectionBounds.RotateSelf( axis.Transpose() );
|
||||
localInfo.material = info.material;
|
||||
localInfo.parallel = info.parallel;
|
||||
localInfo.fadeDepth = info.fadeDepth;
|
||||
localInfo.startTime = info.startTime;
|
||||
localInfo.force = info.force;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelDecal::AddWinding
|
||||
=================
|
||||
*/
|
||||
void idRenderModelDecal::AddWinding( const idWinding &w, const idMaterial *decalMaterial, const idPlane fadePlanes[2], float fadeDepth, int startTime ) {
|
||||
int i;
|
||||
float invFadeDepth, fade;
|
||||
decalInfo_t decalInfo;
|
||||
|
||||
if ( ( material == NULL || material == decalMaterial ) &&
|
||||
tri.numVerts + w.GetNumPoints() < MAX_DECAL_VERTS &&
|
||||
tri.numIndexes + ( w.GetNumPoints() - 2 ) * 3 < MAX_DECAL_INDEXES ) {
|
||||
|
||||
material = decalMaterial;
|
||||
|
||||
// add to this decal
|
||||
decalInfo = material->GetDecalInfo();
|
||||
invFadeDepth = -1.0f / fadeDepth;
|
||||
|
||||
for ( i = 0; i < w.GetNumPoints(); i++ ) {
|
||||
fade = fadePlanes[0].Distance( w[i].ToVec3() ) * invFadeDepth;
|
||||
if ( fade < 0.0f ) {
|
||||
fade = fadePlanes[1].Distance( w[i].ToVec3() ) * invFadeDepth;
|
||||
}
|
||||
if ( fade < 0.0f ) {
|
||||
fade = 0.0f;
|
||||
} else if ( fade > 0.99f ) {
|
||||
fade = 1.0f;
|
||||
}
|
||||
fade = 1.0f - fade;
|
||||
vertDepthFade[tri.numVerts + i] = fade;
|
||||
tri.verts[tri.numVerts + i].xyz = w[i].ToVec3();
|
||||
tri.verts[tri.numVerts + i].st[0] = w[i].s;
|
||||
tri.verts[tri.numVerts + i].st[1] = w[i].t;
|
||||
for ( int k = 0 ; k < 4 ; k++ ) {
|
||||
int icolor = idMath::FtoiFast( decalInfo.start[k] * fade * 255.0f );
|
||||
if ( icolor < 0 ) {
|
||||
icolor = 0;
|
||||
} else if ( icolor > 255 ) {
|
||||
icolor = 255;
|
||||
}
|
||||
tri.verts[tri.numVerts + i].color[k] = icolor;
|
||||
}
|
||||
}
|
||||
for ( i = 2; i < w.GetNumPoints(); i++ ) {
|
||||
tri.indexes[tri.numIndexes + 0] = tri.numVerts;
|
||||
tri.indexes[tri.numIndexes + 1] = tri.numVerts + i - 1;
|
||||
tri.indexes[tri.numIndexes + 2] = tri.numVerts + i;
|
||||
indexStartTime[tri.numIndexes] =
|
||||
indexStartTime[tri.numIndexes + 1] =
|
||||
indexStartTime[tri.numIndexes + 2] = startTime;
|
||||
tri.numIndexes += 3;
|
||||
}
|
||||
tri.numVerts += w.GetNumPoints();
|
||||
return;
|
||||
}
|
||||
|
||||
// if we are at the end of the list, create a new decal
|
||||
if ( !nextDecal ) {
|
||||
nextDecal = idRenderModelDecal::Alloc();
|
||||
}
|
||||
// let the next decal on the chain take a look
|
||||
nextDecal->AddWinding( w, decalMaterial, fadePlanes, fadeDepth, startTime );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelDecal::AddDepthFadedWinding
|
||||
=================
|
||||
*/
|
||||
void idRenderModelDecal::AddDepthFadedWinding( const idWinding &w, const idMaterial *decalMaterial, const idPlane fadePlanes[2], float fadeDepth, int startTime ) {
|
||||
idFixedWinding front, back;
|
||||
|
||||
front = w;
|
||||
if ( front.Split( &back, fadePlanes[0], 0.1f ) == SIDE_CROSS ) {
|
||||
AddWinding( back, decalMaterial, fadePlanes, fadeDepth, startTime );
|
||||
}
|
||||
|
||||
if ( front.Split( &back, fadePlanes[1], 0.1f ) == SIDE_CROSS ) {
|
||||
AddWinding( back, decalMaterial, fadePlanes, fadeDepth, startTime );
|
||||
}
|
||||
|
||||
AddWinding( front, decalMaterial, fadePlanes, fadeDepth, startTime );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelDecal::CreateDecal
|
||||
=================
|
||||
*/
|
||||
void idRenderModelDecal::CreateDecal( const idRenderModel *model, const decalProjectionInfo_t &localInfo ) {
|
||||
|
||||
// check all model surfaces
|
||||
for ( int surfNum = 0; surfNum < model->NumSurfaces(); surfNum++ ) {
|
||||
const modelSurface_t *surf = model->Surface( surfNum );
|
||||
|
||||
// if no geometry or no shader
|
||||
if ( !surf->geometry || !surf->shader ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// decals and overlays use the same rules
|
||||
if ( !localInfo.force && !surf->shader->AllowOverlays() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
srfTriangles_t *stri = surf->geometry;
|
||||
|
||||
// if the triangle bounds do not overlap with projection bounds
|
||||
if ( !localInfo.projectionBounds.IntersectsBounds( stri->bounds ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// allocate memory for the cull bits
|
||||
byte *cullBits = (byte *)_alloca16( stri->numVerts * sizeof( cullBits[0] ) );
|
||||
|
||||
// catagorize all points by the planes
|
||||
SIMDProcessor->DecalPointCull( cullBits, localInfo.boundingPlanes, stri->verts, stri->numVerts );
|
||||
|
||||
// find triangles inside the projection volume
|
||||
for ( int triNum = 0, index = 0; index < stri->numIndexes; index += 3, triNum++ ) {
|
||||
int v1 = stri->indexes[index+0];
|
||||
int v2 = stri->indexes[index+1];
|
||||
int v3 = stri->indexes[index+2];
|
||||
|
||||
// skip triangles completely off one side
|
||||
if ( cullBits[v1] & cullBits[v2] & cullBits[v3] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip back facing triangles
|
||||
if ( stri->facePlanes && stri->facePlanesCalculated &&
|
||||
stri->facePlanes[triNum].Normal() * localInfo.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 2].Normal() < -0.1f ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// create a winding with texture coordinates for the triangle
|
||||
idFixedWinding fw;
|
||||
fw.SetNumPoints( 3 );
|
||||
if ( localInfo.parallel ) {
|
||||
for ( int j = 0; j < 3; j++ ) {
|
||||
fw[j] = stri->verts[stri->indexes[index+j]].xyz;
|
||||
fw[j].s = localInfo.textureAxis[0].Distance( fw[j].ToVec3() );
|
||||
fw[j].t = localInfo.textureAxis[1].Distance( fw[j].ToVec3() );
|
||||
}
|
||||
} else {
|
||||
for ( int j = 0; j < 3; j++ ) {
|
||||
idVec3 dir;
|
||||
float scale;
|
||||
|
||||
fw[j] = stri->verts[stri->indexes[index+j]].xyz;
|
||||
dir = fw[j].ToVec3() - localInfo.projectionOrigin;
|
||||
localInfo.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 1].RayIntersection( fw[j].ToVec3(), dir, scale );
|
||||
dir = fw[j].ToVec3() + scale * dir;
|
||||
fw[j].s = localInfo.textureAxis[0].Distance( dir );
|
||||
fw[j].t = localInfo.textureAxis[1].Distance( dir );
|
||||
}
|
||||
}
|
||||
|
||||
int orBits = cullBits[v1] | cullBits[v2] | cullBits[v3];
|
||||
|
||||
// clip the exact surface triangle to the projection volume
|
||||
for ( int j = 0; j < NUM_DECAL_BOUNDING_PLANES; j++ ) {
|
||||
if ( orBits & ( 1 << j ) ) {
|
||||
if ( !fw.ClipInPlace( -localInfo.boundingPlanes[j] ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( fw.GetNumPoints() == 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddDepthFadedWinding( fw, localInfo.material, localInfo.fadePlanes, localInfo.fadeDepth, localInfo.startTime );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
idRenderModelDecal::RemoveFadedDecals
|
||||
=====================
|
||||
*/
|
||||
idRenderModelDecal *idRenderModelDecal::RemoveFadedDecals( idRenderModelDecal *decals, int time ) {
|
||||
int i, j, minTime, newNumIndexes, newNumVerts;
|
||||
int inUse[MAX_DECAL_VERTS];
|
||||
decalInfo_t decalInfo;
|
||||
idRenderModelDecal *nextDecal;
|
||||
|
||||
if ( decals == NULL ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// recursively free any next decals
|
||||
decals->nextDecal = RemoveFadedDecals( decals->nextDecal, time );
|
||||
|
||||
// free the decals if no material set
|
||||
if ( decals->material == NULL ) {
|
||||
nextDecal = decals->nextDecal;
|
||||
Free( decals );
|
||||
return nextDecal;
|
||||
}
|
||||
|
||||
decalInfo = decals->material->GetDecalInfo();
|
||||
minTime = time - ( decalInfo.stayTime + decalInfo.fadeTime );
|
||||
|
||||
newNumIndexes = 0;
|
||||
for ( i = 0; i < decals->tri.numIndexes; i += 3 ) {
|
||||
if ( decals->indexStartTime[i] > minTime ) {
|
||||
// keep this triangle
|
||||
if ( newNumIndexes != i ) {
|
||||
for ( j = 0; j < 3; j++ ) {
|
||||
decals->tri.indexes[newNumIndexes+j] = decals->tri.indexes[i+j];
|
||||
decals->indexStartTime[newNumIndexes+j] = decals->indexStartTime[i+j];
|
||||
}
|
||||
}
|
||||
newNumIndexes += 3;
|
||||
}
|
||||
}
|
||||
|
||||
// free the decals if all trianges faded away
|
||||
if ( newNumIndexes == 0 ) {
|
||||
nextDecal = decals->nextDecal;
|
||||
Free( decals );
|
||||
return nextDecal;
|
||||
}
|
||||
|
||||
decals->tri.numIndexes = newNumIndexes;
|
||||
|
||||
memset( inUse, 0, sizeof( inUse ) );
|
||||
for ( i = 0; i < decals->tri.numIndexes; i++ ) {
|
||||
inUse[decals->tri.indexes[i]] = 1;
|
||||
}
|
||||
|
||||
newNumVerts = 0;
|
||||
for ( i = 0; i < decals->tri.numVerts; i++ ) {
|
||||
if ( !inUse[i] ) {
|
||||
continue;
|
||||
}
|
||||
decals->tri.verts[newNumVerts] = decals->tri.verts[i];
|
||||
decals->vertDepthFade[newNumVerts] = decals->vertDepthFade[i];
|
||||
inUse[i] = newNumVerts;
|
||||
newNumVerts++;
|
||||
}
|
||||
decals->tri.numVerts = newNumVerts;
|
||||
|
||||
for ( i = 0; i < decals->tri.numIndexes; i++ ) {
|
||||
decals->tri.indexes[i] = inUse[decals->tri.indexes[i]];
|
||||
}
|
||||
|
||||
return decals;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
idRenderModelDecal::AddDecalDrawSurf
|
||||
=====================
|
||||
*/
|
||||
void idRenderModelDecal::AddDecalDrawSurf( viewEntity_t *space ) {
|
||||
int i, j, maxTime;
|
||||
float f;
|
||||
decalInfo_t decalInfo;
|
||||
|
||||
if ( tri.numIndexes == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fade down all the verts with time
|
||||
decalInfo = material->GetDecalInfo();
|
||||
maxTime = decalInfo.stayTime + decalInfo.fadeTime;
|
||||
|
||||
// set vertex colors and remove faded triangles
|
||||
for ( i = 0 ; i < tri.numIndexes ; i += 3 ) {
|
||||
int deltaTime = tr.viewDef->renderView.time - indexStartTime[i];
|
||||
|
||||
if ( deltaTime > maxTime ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( deltaTime <= decalInfo.stayTime ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
deltaTime -= decalInfo.stayTime;
|
||||
f = (float)deltaTime / decalInfo.fadeTime;
|
||||
|
||||
for ( j = 0; j < 3; j++ ) {
|
||||
int ind = tri.indexes[i+j];
|
||||
|
||||
for ( int k = 0; k < 4; k++ ) {
|
||||
float fcolor = decalInfo.start[k] + ( decalInfo.end[k] - decalInfo.start[k] ) * f;
|
||||
int icolor = idMath::FtoiFast( fcolor * vertDepthFade[ind] * 255.0f );
|
||||
if ( icolor < 0 ) {
|
||||
icolor = 0;
|
||||
} else if ( icolor > 255 ) {
|
||||
icolor = 255;
|
||||
}
|
||||
tri.verts[ind].color[k] = icolor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy the tri and indexes to temp heap memory,
|
||||
// because if we are running multi-threaded, we wouldn't
|
||||
// be able to reorganize the index list
|
||||
srfTriangles_t *newTri = (srfTriangles_t *)R_FrameAlloc( sizeof( *newTri ) );
|
||||
*newTri = tri;
|
||||
|
||||
// copy the current vertexes to temp vertex cache
|
||||
newTri->ambientCache = vertexCache.AllocFrameTemp( tri.verts, tri.numVerts * sizeof( idDrawVert ) );
|
||||
|
||||
// create the drawsurf
|
||||
R_AddDrawSurf( newTri, space, &space->entityDef->parms, material, space->scissorRect );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelDecal::ReadFromDemoFile
|
||||
====================
|
||||
*/
|
||||
void idRenderModelDecal::ReadFromDemoFile( idDemoFile *f ) {
|
||||
// FIXME: implement
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelDecal::WriteToDemoFile
|
||||
====================
|
||||
*/
|
||||
void idRenderModelDecal::WriteToDemoFile( idDemoFile *f ) const {
|
||||
// FIXME: implement
|
||||
}
|
||||
116
neo/renderer/ModelDecal.h
Normal file
116
neo/renderer/ModelDecal.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __MODELDECAL_H__
|
||||
#define __MODELDECAL_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Decals are lightweight primitives for bullet / blood marks.
|
||||
Decals with common materials will be merged together, but additional
|
||||
decals will be allocated as needed. The material should not be
|
||||
one that receives lighting, because no interactions are generated
|
||||
for these lightweight surfaces.
|
||||
|
||||
FIXME: Decals on models in portalled off areas do not get freed
|
||||
until the area becomes visible again.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
const int NUM_DECAL_BOUNDING_PLANES = 6;
|
||||
|
||||
typedef struct decalProjectionInfo_s {
|
||||
idVec3 projectionOrigin;
|
||||
idBounds projectionBounds;
|
||||
idPlane boundingPlanes[6];
|
||||
idPlane fadePlanes[2];
|
||||
idPlane textureAxis[2];
|
||||
const idMaterial * material;
|
||||
bool parallel;
|
||||
float fadeDepth;
|
||||
int startTime;
|
||||
bool force;
|
||||
} decalProjectionInfo_t;
|
||||
|
||||
|
||||
class idRenderModelDecal {
|
||||
public:
|
||||
idRenderModelDecal( void );
|
||||
~idRenderModelDecal( void );
|
||||
|
||||
static idRenderModelDecal * Alloc( void );
|
||||
static void Free( idRenderModelDecal *decal );
|
||||
|
||||
// Creates decal projection info.
|
||||
static bool CreateProjectionInfo( decalProjectionInfo_t &info, const idFixedWinding &winding, const idVec3 &projectionOrigin, const bool parallel, const float fadeDepth, const idMaterial *material, const int startTime );
|
||||
|
||||
// Transform the projection info from global space to local.
|
||||
static void GlobalProjectionInfoToLocal( decalProjectionInfo_t &localInfo, const decalProjectionInfo_t &info, const idVec3 &origin, const idMat3 &axis );
|
||||
|
||||
// Creates a deal on the given model.
|
||||
void CreateDecal( const idRenderModel *model, const decalProjectionInfo_t &localInfo );
|
||||
|
||||
// Remove decals that are completely faded away.
|
||||
static idRenderModelDecal * RemoveFadedDecals( idRenderModelDecal *decals, int time );
|
||||
|
||||
// Updates the vertex colors, removing any faded indexes,
|
||||
// then copy the verts to temporary vertex cache and adds a drawSurf.
|
||||
void AddDecalDrawSurf( struct viewEntity_s *space );
|
||||
|
||||
// Returns the next decal in the chain.
|
||||
idRenderModelDecal * Next( void ) const { return nextDecal; }
|
||||
|
||||
void ReadFromDemoFile( class idDemoFile *f );
|
||||
void WriteToDemoFile( class idDemoFile *f ) const;
|
||||
|
||||
private:
|
||||
static const int MAX_DECAL_VERTS = 40;
|
||||
static const int MAX_DECAL_INDEXES = 60;
|
||||
|
||||
const idMaterial * material;
|
||||
srfTriangles_t tri;
|
||||
idDrawVert verts[MAX_DECAL_VERTS];
|
||||
float vertDepthFade[MAX_DECAL_VERTS];
|
||||
glIndex_t indexes[MAX_DECAL_INDEXES];
|
||||
int indexStartTime[MAX_DECAL_INDEXES];
|
||||
idRenderModelDecal * nextDecal;
|
||||
|
||||
// Adds the winding triangles to the appropriate decal in the
|
||||
// chain, creating a new one if necessary.
|
||||
void AddWinding( const idWinding &w, const idMaterial *decalMaterial, const idPlane fadePlanes[2], float fadeDepth, int startTime );
|
||||
|
||||
// Adds depth faded triangles for the winding to the appropriate
|
||||
// decal in the chain, creating a new one if necessary.
|
||||
// The part of the winding at the front side of both fade planes is not faded.
|
||||
// The parts at the back sides of the fade planes are faded with the given depth.
|
||||
void AddDepthFadedWinding( const idWinding &w, const idMaterial *decalMaterial, const idPlane fadePlanes[2], float fadeDepth, int startTime );
|
||||
};
|
||||
|
||||
#endif /* !__MODELDECAL_H__ */
|
||||
622
neo/renderer/ModelManager.cpp
Normal file
622
neo/renderer/ModelManager.cpp
Normal file
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "Model_local.h"
|
||||
#include "tr_local.h" // just for R_FreeWorldInteractions and R_CreateWorldInteractions
|
||||
|
||||
|
||||
class idRenderModelManagerLocal : public idRenderModelManager {
|
||||
public:
|
||||
idRenderModelManagerLocal();
|
||||
virtual ~idRenderModelManagerLocal() {}
|
||||
|
||||
virtual void Init();
|
||||
virtual void Shutdown();
|
||||
virtual idRenderModel * AllocModel();
|
||||
virtual void FreeModel( idRenderModel *model );
|
||||
virtual idRenderModel * FindModel( const char *modelName );
|
||||
virtual idRenderModel * CheckModel( const char *modelName );
|
||||
virtual idRenderModel * DefaultModel();
|
||||
virtual void AddModel( idRenderModel *model );
|
||||
virtual void RemoveModel( idRenderModel *model );
|
||||
virtual void ReloadModels( bool forceAll = false );
|
||||
virtual void FreeModelVertexCaches();
|
||||
virtual void WritePrecacheCommands( idFile *file );
|
||||
virtual void BeginLevelLoad();
|
||||
virtual void EndLevelLoad();
|
||||
|
||||
virtual void PrintMemInfo( MemInfo_t *mi );
|
||||
|
||||
private:
|
||||
idList<idRenderModel*> models;
|
||||
idHashIndex hash;
|
||||
idRenderModel * defaultModel;
|
||||
idRenderModel * beamModel;
|
||||
idRenderModel * spriteModel;
|
||||
idRenderModel * trailModel;
|
||||
bool insideLevelLoad; // don't actually load now
|
||||
|
||||
idRenderModel * GetModel( const char *modelName, bool createIfNotFound );
|
||||
|
||||
static void PrintModel_f( const idCmdArgs &args );
|
||||
static void ListModels_f( const idCmdArgs &args );
|
||||
static void ReloadModels_f( const idCmdArgs &args );
|
||||
static void TouchModel_f( const idCmdArgs &args );
|
||||
};
|
||||
|
||||
|
||||
idRenderModelManagerLocal localModelManager;
|
||||
idRenderModelManager * renderModelManager = &localModelManager;
|
||||
|
||||
/*
|
||||
==============
|
||||
idRenderModelManagerLocal::idRenderModelManagerLocal
|
||||
==============
|
||||
*/
|
||||
idRenderModelManagerLocal::idRenderModelManagerLocal() {
|
||||
defaultModel = NULL;
|
||||
beamModel = NULL;
|
||||
spriteModel = NULL;
|
||||
insideLevelLoad = false;
|
||||
trailModel = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
idRenderModelManagerLocal::PrintModel_f
|
||||
==============
|
||||
*/
|
||||
void idRenderModelManagerLocal::PrintModel_f( const idCmdArgs &args ) {
|
||||
idRenderModel *model;
|
||||
|
||||
if ( args.Argc() != 2 ) {
|
||||
common->Printf( "usage: printModel <modelName>\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
model = renderModelManager->CheckModel( args.Argv( 1 ) );
|
||||
if ( !model ) {
|
||||
common->Printf( "model \"%s\" not found\n", args.Argv( 1 ) );
|
||||
return;
|
||||
}
|
||||
|
||||
model->Print();
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
idRenderModelManagerLocal::ListModels_f
|
||||
==============
|
||||
*/
|
||||
void idRenderModelManagerLocal::ListModels_f( const idCmdArgs &args ) {
|
||||
int totalMem = 0;
|
||||
int inUse = 0;
|
||||
|
||||
common->Printf( " mem srf verts tris\n" );
|
||||
common->Printf( " --- --- ----- ----\n" );
|
||||
|
||||
for ( int i = 0 ; i < localModelManager.models.Num() ; i++ ) {
|
||||
idRenderModel *model = localModelManager.models[i];
|
||||
|
||||
if ( !model->IsLoaded() ) {
|
||||
continue;
|
||||
}
|
||||
model->List();
|
||||
totalMem += model->Memory();
|
||||
inUse++;
|
||||
}
|
||||
|
||||
common->Printf( " --- --- ----- ----\n" );
|
||||
common->Printf( " mem srf verts tris\n" );
|
||||
|
||||
common->Printf( "%i loaded models\n", inUse );
|
||||
common->Printf( "total memory: %4.1fM\n", (float)totalMem / (1024*1024) );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
idRenderModelManagerLocal::ReloadModels_f
|
||||
==============
|
||||
*/
|
||||
void idRenderModelManagerLocal::ReloadModels_f( const idCmdArgs &args ) {
|
||||
if ( idStr::Icmp( args.Argv(1), "all" ) == 0 ) {
|
||||
localModelManager.ReloadModels( true );
|
||||
} else {
|
||||
localModelManager.ReloadModels( false );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
idRenderModelManagerLocal::TouchModel_f
|
||||
|
||||
Precache a specific model
|
||||
==============
|
||||
*/
|
||||
void idRenderModelManagerLocal::TouchModel_f( const idCmdArgs &args ) {
|
||||
const char *model = args.Argv( 1 );
|
||||
|
||||
if ( !model[0] ) {
|
||||
common->Printf( "usage: touchModel <modelName>\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
common->Printf( "touchModel %s\n", model );
|
||||
session->UpdateScreen();
|
||||
idRenderModel *m = renderModelManager->CheckModel( model );
|
||||
if ( !m ) {
|
||||
common->Printf( "...not found\n" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::WritePrecacheCommands
|
||||
=================
|
||||
*/
|
||||
void idRenderModelManagerLocal::WritePrecacheCommands( idFile *f ) {
|
||||
for ( int i = 0 ; i < models.Num() ; i++ ) {
|
||||
idRenderModel *model = models[i];
|
||||
|
||||
if ( !model ) {
|
||||
continue;
|
||||
}
|
||||
if ( !model->IsReloadable() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char str[1024];
|
||||
sprintf( str, "touchModel %s\n", model->Name() );
|
||||
common->Printf( "%s", str );
|
||||
f->Printf( "%s", str );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::Init
|
||||
=================
|
||||
*/
|
||||
void idRenderModelManagerLocal::Init() {
|
||||
cmdSystem->AddCommand( "listModels", ListModels_f, CMD_FL_RENDERER, "lists all models" );
|
||||
cmdSystem->AddCommand( "printModel", PrintModel_f, CMD_FL_RENDERER, "prints model info", idCmdSystem::ArgCompletion_ModelName );
|
||||
cmdSystem->AddCommand( "reloadModels", ReloadModels_f, CMD_FL_RENDERER|CMD_FL_CHEAT, "reloads models" );
|
||||
cmdSystem->AddCommand( "touchModel", TouchModel_f, CMD_FL_RENDERER, "touches a model", idCmdSystem::ArgCompletion_ModelName );
|
||||
|
||||
insideLevelLoad = false;
|
||||
|
||||
// create a default model
|
||||
idRenderModelStatic *model = new idRenderModelStatic;
|
||||
model->InitEmpty( "_DEFAULT" );
|
||||
model->MakeDefaultModel();
|
||||
model->SetLevelLoadReferenced( true );
|
||||
defaultModel = model;
|
||||
AddModel( model );
|
||||
|
||||
// create the beam model
|
||||
idRenderModelStatic *beam = new idRenderModelBeam;
|
||||
beam->InitEmpty( "_BEAM" );
|
||||
beam->SetLevelLoadReferenced( true );
|
||||
beamModel = beam;
|
||||
AddModel( beam );
|
||||
|
||||
idRenderModelStatic *sprite = new idRenderModelSprite;
|
||||
sprite->InitEmpty( "_SPRITE" );
|
||||
sprite->SetLevelLoadReferenced( true );
|
||||
spriteModel = sprite;
|
||||
AddModel( sprite );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::Shutdown
|
||||
=================
|
||||
*/
|
||||
void idRenderModelManagerLocal::Shutdown() {
|
||||
models.DeleteContents( true );
|
||||
hash.Free();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::GetModel
|
||||
=================
|
||||
*/
|
||||
idRenderModel *idRenderModelManagerLocal::GetModel( const char *modelName, bool createIfNotFound ) {
|
||||
idStr canonical;
|
||||
idStr extension;
|
||||
|
||||
if ( !modelName || !modelName[0] ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
canonical = modelName;
|
||||
canonical.ToLower();
|
||||
|
||||
// see if it is already present
|
||||
int key = hash.GenerateKey( modelName, false );
|
||||
for ( int i = hash.First( key ); i != -1; i = hash.Next( i ) ) {
|
||||
idRenderModel *model = models[i];
|
||||
|
||||
if ( canonical.Icmp( model->Name() ) == 0 ) {
|
||||
if ( !model->IsLoaded() ) {
|
||||
// reload it if it was purged
|
||||
model->LoadModel();
|
||||
} else if ( insideLevelLoad && !model->IsLevelLoadReferenced() ) {
|
||||
// we are reusing a model already in memory, but
|
||||
// touch all the materials to make sure they stay
|
||||
// in memory as well
|
||||
model->TouchData();
|
||||
}
|
||||
model->SetLevelLoadReferenced( true );
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
// see if we can load it
|
||||
|
||||
// determine which subclass of idRenderModel to initialize
|
||||
|
||||
idRenderModel *model;
|
||||
|
||||
canonical.ExtractFileExtension( extension );
|
||||
|
||||
if ( ( extension.Icmp( "ase" ) == 0 ) || ( extension.Icmp( "lwo" ) == 0 ) || ( extension.Icmp( "flt" ) == 0 ) ) {
|
||||
model = new idRenderModelStatic;
|
||||
model->InitFromFile( modelName );
|
||||
} else if ( extension.Icmp( "ma" ) == 0 ) {
|
||||
model = new idRenderModelStatic;
|
||||
model->InitFromFile( modelName );
|
||||
} else if ( extension.Icmp( MD5_MESH_EXT ) == 0 ) {
|
||||
model = new idRenderModelMD5;
|
||||
model->InitFromFile( modelName );
|
||||
} else if ( extension.Icmp( "md3" ) == 0 ) {
|
||||
model = new idRenderModelMD3;
|
||||
model->InitFromFile( modelName );
|
||||
} else if ( extension.Icmp( "prt" ) == 0 ) {
|
||||
model = new idRenderModelPrt;
|
||||
model->InitFromFile( modelName );
|
||||
} else if ( extension.Icmp( "liquid" ) == 0 ) {
|
||||
model = new idRenderModelLiquid;
|
||||
model->InitFromFile( modelName );
|
||||
} else {
|
||||
|
||||
if ( extension.Length() ) {
|
||||
common->Warning( "unknown model type '%s'", canonical.c_str() );
|
||||
}
|
||||
|
||||
if ( !createIfNotFound ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idRenderModelStatic *smodel = new idRenderModelStatic;
|
||||
smodel->InitEmpty( modelName );
|
||||
smodel->MakeDefaultModel();
|
||||
|
||||
model = smodel;
|
||||
}
|
||||
|
||||
model->SetLevelLoadReferenced( true );
|
||||
|
||||
if ( !createIfNotFound && model->IsDefaultModel() ) {
|
||||
delete model;
|
||||
model = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AddModel( model );
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::AllocModel
|
||||
=================
|
||||
*/
|
||||
idRenderModel *idRenderModelManagerLocal::AllocModel() {
|
||||
return new idRenderModelStatic();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::FreeModel
|
||||
=================
|
||||
*/
|
||||
void idRenderModelManagerLocal::FreeModel( idRenderModel *model ) {
|
||||
if ( !model ) {
|
||||
return;
|
||||
}
|
||||
if ( !dynamic_cast<idRenderModelStatic *>( model ) ) {
|
||||
common->Error( "idRenderModelManager::FreeModel: model '%s' is not a static model", model->Name() );
|
||||
return;
|
||||
}
|
||||
if ( model == defaultModel ) {
|
||||
common->Error( "idRenderModelManager::FreeModel: can't free the default model" );
|
||||
return;
|
||||
}
|
||||
if ( model == beamModel ) {
|
||||
common->Error( "idRenderModelManager::FreeModel: can't free the beam model" );
|
||||
return;
|
||||
}
|
||||
if ( model == spriteModel ) {
|
||||
common->Error( "idRenderModelManager::FreeModel: can't free the sprite model" );
|
||||
return;
|
||||
}
|
||||
|
||||
R_CheckForEntityDefsUsingModel( model );
|
||||
|
||||
delete model;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::FindModel
|
||||
=================
|
||||
*/
|
||||
idRenderModel *idRenderModelManagerLocal::FindModel( const char *modelName ) {
|
||||
return GetModel( modelName, true );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::CheckModel
|
||||
=================
|
||||
*/
|
||||
idRenderModel *idRenderModelManagerLocal::CheckModel( const char *modelName ) {
|
||||
return GetModel( modelName, false );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::DefaultModel
|
||||
=================
|
||||
*/
|
||||
idRenderModel *idRenderModelManagerLocal::DefaultModel() {
|
||||
return defaultModel;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::AddModel
|
||||
=================
|
||||
*/
|
||||
void idRenderModelManagerLocal::AddModel( idRenderModel *model ) {
|
||||
hash.Add( hash.GenerateKey( model->Name(), false ), models.Append( model ) );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::RemoveModel
|
||||
=================
|
||||
*/
|
||||
void idRenderModelManagerLocal::RemoveModel( idRenderModel *model ) {
|
||||
int index = models.FindIndex( model );
|
||||
hash.RemoveIndex( hash.GenerateKey( model->Name(), false ), index );
|
||||
models.RemoveIndex( index );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::ReloadModels
|
||||
=================
|
||||
*/
|
||||
void idRenderModelManagerLocal::ReloadModels( bool forceAll ) {
|
||||
if ( forceAll ) {
|
||||
common->Printf( "Reloading all model files...\n" );
|
||||
} else {
|
||||
common->Printf( "Checking for changed model files...\n" );
|
||||
}
|
||||
|
||||
R_FreeDerivedData();
|
||||
|
||||
// skip the default model at index 0
|
||||
for ( int i = 1 ; i < models.Num() ; i++ ) {
|
||||
idRenderModel *model = models[i];
|
||||
|
||||
// we may want to allow world model reloading in the future, but we don't now
|
||||
if ( !model->IsReloadable() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !forceAll ) {
|
||||
// check timestamp
|
||||
ID_TIME_T current;
|
||||
|
||||
fileSystem->ReadFile( model->Name(), NULL, ¤t );
|
||||
if ( current <= model->Timestamp() ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
common->DPrintf( "reloading %s.\n", model->Name() );
|
||||
|
||||
model->LoadModel();
|
||||
}
|
||||
|
||||
// we must force the world to regenerate, because models may
|
||||
// have changed size, making their references invalid
|
||||
R_ReCreateWorldReferences();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::FreeModelVertexCaches
|
||||
=================
|
||||
*/
|
||||
void idRenderModelManagerLocal::FreeModelVertexCaches() {
|
||||
for ( int i = 0 ; i < models.Num() ; i++ ) {
|
||||
idRenderModel *model = models[i];
|
||||
model->FreeVertexCache();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::BeginLevelLoad
|
||||
=================
|
||||
*/
|
||||
void idRenderModelManagerLocal::BeginLevelLoad() {
|
||||
insideLevelLoad = true;
|
||||
|
||||
for ( int i = 0 ; i < models.Num() ; i++ ) {
|
||||
idRenderModel *model = models[i];
|
||||
|
||||
if ( com_purgeAll.GetBool() && model->IsReloadable() ) {
|
||||
R_CheckForEntityDefsUsingModel( model );
|
||||
model->PurgeModel();
|
||||
}
|
||||
|
||||
model->SetLevelLoadReferenced( false );
|
||||
}
|
||||
|
||||
// purge unused triangle surface memory
|
||||
R_PurgeTriSurfData( frameData );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::EndLevelLoad
|
||||
=================
|
||||
*/
|
||||
void idRenderModelManagerLocal::EndLevelLoad() {
|
||||
common->Printf( "----- idRenderModelManagerLocal::EndLevelLoad -----\n" );
|
||||
|
||||
int start = Sys_Milliseconds();
|
||||
|
||||
insideLevelLoad = false;
|
||||
int purgeCount = 0;
|
||||
int keepCount = 0;
|
||||
int loadCount = 0;
|
||||
|
||||
// purge any models not touched
|
||||
for ( int i = 0 ; i < models.Num() ; i++ ) {
|
||||
idRenderModel *model = models[i];
|
||||
|
||||
if ( !model->IsLevelLoadReferenced() && model->IsLoaded() && model->IsReloadable() ) {
|
||||
|
||||
// common->Printf( "purging %s\n", model->Name() );
|
||||
|
||||
purgeCount++;
|
||||
|
||||
R_CheckForEntityDefsUsingModel( model );
|
||||
|
||||
model->PurgeModel();
|
||||
|
||||
} else {
|
||||
|
||||
// common->Printf( "keeping %s\n", model->Name() );
|
||||
|
||||
keepCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// purge unused triangle surface memory
|
||||
R_PurgeTriSurfData( frameData );
|
||||
|
||||
// load any new ones
|
||||
for ( int i = 0 ; i < models.Num() ; i++ ) {
|
||||
idRenderModel *model = models[i];
|
||||
|
||||
if ( model->IsLevelLoadReferenced() && !model->IsLoaded() && model->IsReloadable() ) {
|
||||
|
||||
loadCount++;
|
||||
model->LoadModel();
|
||||
|
||||
if ( ( loadCount & 15 ) == 0 ) {
|
||||
session->PacifierUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _D3XP added this
|
||||
int end = Sys_Milliseconds();
|
||||
common->Printf( "%5i models purged from previous level, ", purgeCount );
|
||||
common->Printf( "%5i models kept.\n", keepCount );
|
||||
if ( loadCount ) {
|
||||
common->Printf( "%5i new models loaded in %5.1f seconds\n", loadCount, (end-start) * 0.001 );
|
||||
}
|
||||
common->Printf( "---------------------------------------------------\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelManagerLocal::PrintMemInfo
|
||||
=================
|
||||
*/
|
||||
void idRenderModelManagerLocal::PrintMemInfo( MemInfo_t *mi ) {
|
||||
int i, j, totalMem = 0;
|
||||
int *sortIndex;
|
||||
idFile *f;
|
||||
|
||||
f = fileSystem->OpenFileWrite( mi->filebase + "_models.txt" );
|
||||
if ( !f ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// sort first
|
||||
sortIndex = new int[ localModelManager.models.Num()];
|
||||
|
||||
for ( i = 0; i < localModelManager.models.Num(); i++ ) {
|
||||
sortIndex[i] = i;
|
||||
}
|
||||
|
||||
for ( i = 0; i < localModelManager.models.Num() - 1; i++ ) {
|
||||
for ( j = i + 1; j < localModelManager.models.Num(); j++ ) {
|
||||
if ( localModelManager.models[sortIndex[i]]->Memory() < localModelManager.models[sortIndex[j]]->Memory() ) {
|
||||
int temp = sortIndex[i];
|
||||
sortIndex[i] = sortIndex[j];
|
||||
sortIndex[j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print next
|
||||
for ( int i = 0 ; i < localModelManager.models.Num() ; i++ ) {
|
||||
idRenderModel *model = localModelManager.models[sortIndex[i]];
|
||||
int mem;
|
||||
|
||||
if ( !model->IsLoaded() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mem = model->Memory();
|
||||
totalMem += mem;
|
||||
f->Printf( "%s %s\n", idStr::FormatNumber( mem ).c_str(), model->Name() );
|
||||
}
|
||||
|
||||
delete sortIndex;
|
||||
mi->modelAssetsTotal = totalMem;
|
||||
|
||||
f->Printf( "\nTotal model bytes allocated: %s\n", idStr::FormatNumber( totalMem ).c_str() );
|
||||
fileSystem->CloseFile( f );
|
||||
}
|
||||
99
neo/renderer/ModelManager.h
Normal file
99
neo/renderer/ModelManager.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __MODELMANAGER_H__
|
||||
#define __MODELMANAGER_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Model Manager
|
||||
|
||||
Temporarily created models do not need to be added to the model manager.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idRenderModelManager {
|
||||
public:
|
||||
virtual ~idRenderModelManager() {}
|
||||
|
||||
// registers console commands and clears the list
|
||||
virtual void Init() = 0;
|
||||
|
||||
// frees all the models
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
// called only by renderer::BeginLevelLoad
|
||||
virtual void BeginLevelLoad() = 0;
|
||||
|
||||
// called only by renderer::EndLevelLoad
|
||||
virtual void EndLevelLoad() = 0;
|
||||
|
||||
// allocates a new empty render model.
|
||||
virtual idRenderModel * AllocModel() = 0;
|
||||
|
||||
// frees a render model
|
||||
virtual void FreeModel( idRenderModel *model ) = 0;
|
||||
|
||||
// returns NULL if modelName is NULL or an empty string, otherwise
|
||||
// it will create a default model if not loadable
|
||||
virtual idRenderModel * FindModel( const char *modelName ) = 0;
|
||||
|
||||
// returns NULL if not loadable
|
||||
virtual idRenderModel * CheckModel( const char *modelName ) = 0;
|
||||
|
||||
// returns the default cube model
|
||||
virtual idRenderModel * DefaultModel() = 0;
|
||||
|
||||
// world map parsing will add all the inline models with this call
|
||||
virtual void AddModel( idRenderModel *model ) = 0;
|
||||
|
||||
// when a world map unloads, it removes its internal models from the list
|
||||
// before freeing them.
|
||||
// There may be an issue with multiple renderWorlds that share data...
|
||||
virtual void RemoveModel( idRenderModel *model ) = 0;
|
||||
|
||||
// the reloadModels console command calls this, but it can
|
||||
// also be explicitly invoked
|
||||
virtual void ReloadModels( bool forceAll = false ) = 0;
|
||||
|
||||
// write "touchModel <model>" commands for each non-world-map model
|
||||
virtual void WritePrecacheCommands( idFile *f ) = 0;
|
||||
|
||||
// called during vid_restart
|
||||
virtual void FreeModelVertexCaches() = 0;
|
||||
|
||||
// print memory info
|
||||
virtual void PrintMemInfo( MemInfo_t *mi ) = 0;
|
||||
};
|
||||
|
||||
// this will be statically pointed at a private implementation
|
||||
extern idRenderModelManager *renderModelManager;
|
||||
|
||||
#endif /* !__MODELMANAGER_H__ */
|
||||
386
neo/renderer/ModelOverlay.cpp
Normal file
386
neo/renderer/ModelOverlay.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "Model_local.h"
|
||||
#include "tr_local.h"
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelOverlay::idRenderModelOverlay
|
||||
====================
|
||||
*/
|
||||
idRenderModelOverlay::idRenderModelOverlay() {
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelOverlay::~idRenderModelOverlay
|
||||
====================
|
||||
*/
|
||||
idRenderModelOverlay::~idRenderModelOverlay() {
|
||||
int i, k;
|
||||
|
||||
for ( k = 0; k < materials.Num(); k++ ) {
|
||||
for ( i = 0; i < materials[k]->surfaces.Num(); i++ ) {
|
||||
FreeSurface( materials[k]->surfaces[i] );
|
||||
}
|
||||
materials[k]->surfaces.Clear();
|
||||
delete materials[k];
|
||||
}
|
||||
materials.Clear();
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelOverlay::Alloc
|
||||
====================
|
||||
*/
|
||||
idRenderModelOverlay *idRenderModelOverlay::Alloc( void ) {
|
||||
return new idRenderModelOverlay;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelOverlay::Free
|
||||
====================
|
||||
*/
|
||||
void idRenderModelOverlay::Free( idRenderModelOverlay *overlay ) {
|
||||
delete overlay;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelOverlay::FreeSurface
|
||||
====================
|
||||
*/
|
||||
void idRenderModelOverlay::FreeSurface( overlaySurface_t *surface ) {
|
||||
if ( surface->verts ) {
|
||||
Mem_Free( surface->verts );
|
||||
}
|
||||
if ( surface->indexes ) {
|
||||
Mem_Free( surface->indexes );
|
||||
}
|
||||
Mem_Free( surface );
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
idRenderModelOverlay::CreateOverlay
|
||||
|
||||
This projects on both front and back sides to avoid seams
|
||||
The material should be clamped, because entire triangles are added, some of which
|
||||
may extend well past the 0.0 to 1.0 texture range
|
||||
=====================
|
||||
*/
|
||||
void idRenderModelOverlay::CreateOverlay( const idRenderModel *model, const idPlane localTextureAxis[2], const idMaterial *mtr ) {
|
||||
int i, maxVerts, maxIndexes, surfNum;
|
||||
idRenderModelOverlay *overlay = NULL;
|
||||
|
||||
// count up the maximum possible vertices and indexes per surface
|
||||
maxVerts = 0;
|
||||
maxIndexes = 0;
|
||||
for ( surfNum = 0; surfNum < model->NumSurfaces(); surfNum++ ) {
|
||||
const modelSurface_t *surf = model->Surface( surfNum );
|
||||
if ( surf->geometry->numVerts > maxVerts ) {
|
||||
maxVerts = surf->geometry->numVerts;
|
||||
}
|
||||
if ( surf->geometry->numIndexes > maxIndexes ) {
|
||||
maxIndexes = surf->geometry->numIndexes;
|
||||
}
|
||||
}
|
||||
|
||||
// make temporary buffers for the building process
|
||||
overlayVertex_t *overlayVerts = (overlayVertex_t *)_alloca( maxVerts * sizeof( *overlayVerts ) );
|
||||
glIndex_t *overlayIndexes = (glIndex_t *)_alloca16( maxIndexes * sizeof( *overlayIndexes ) );
|
||||
|
||||
// pull out the triangles we need from the base surfaces
|
||||
for ( surfNum = 0; surfNum < model->NumBaseSurfaces(); surfNum++ ) {
|
||||
const modelSurface_t *surf = model->Surface( surfNum );
|
||||
float d;
|
||||
|
||||
if ( !surf->geometry || !surf->shader ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// some surfaces can explicitly disallow overlays
|
||||
if ( !surf->shader->AllowOverlays() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const srfTriangles_t *stri = surf->geometry;
|
||||
|
||||
// try to cull the whole surface along the first texture axis
|
||||
d = stri->bounds.PlaneDistance( localTextureAxis[0] );
|
||||
if ( d < 0.0f || d > 1.0f ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// try to cull the whole surface along the second texture axis
|
||||
d = stri->bounds.PlaneDistance( localTextureAxis[1] );
|
||||
if ( d < 0.0f || d > 1.0f ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
byte *cullBits = (byte *)_alloca16( stri->numVerts * sizeof( cullBits[0] ) );
|
||||
idVec2 *texCoords = (idVec2 *)_alloca16( stri->numVerts * sizeof( texCoords[0] ) );
|
||||
|
||||
SIMDProcessor->OverlayPointCull( cullBits, texCoords, localTextureAxis, stri->verts, stri->numVerts );
|
||||
|
||||
glIndex_t *vertexRemap = (glIndex_t *)_alloca16( sizeof( vertexRemap[0] ) * stri->numVerts );
|
||||
SIMDProcessor->Memset( vertexRemap, -1, sizeof( vertexRemap[0] ) * stri->numVerts );
|
||||
|
||||
// find triangles that need the overlay
|
||||
int numVerts = 0;
|
||||
int numIndexes = 0;
|
||||
int triNum = 0;
|
||||
for ( int index = 0; index < stri->numIndexes; index += 3, triNum++ ) {
|
||||
int v1 = stri->indexes[index+0];
|
||||
int v2 = stri->indexes[index+1];
|
||||
int v3 = stri->indexes[index+2];
|
||||
|
||||
// skip triangles completely off one side
|
||||
if ( cullBits[v1] & cullBits[v2] & cullBits[v3] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// we could do more precise triangle culling, like the light interaction does, if desired
|
||||
|
||||
// keep this triangle
|
||||
for ( int vnum = 0; vnum < 3; vnum++ ) {
|
||||
int ind = stri->indexes[index+vnum];
|
||||
if ( vertexRemap[ind] == (glIndex_t)-1 ) {
|
||||
vertexRemap[ind] = numVerts;
|
||||
|
||||
overlayVerts[numVerts].vertexNum = ind;
|
||||
overlayVerts[numVerts].st[0] = texCoords[ind][0];
|
||||
overlayVerts[numVerts].st[1] = texCoords[ind][1];
|
||||
|
||||
numVerts++;
|
||||
}
|
||||
overlayIndexes[numIndexes++] = vertexRemap[ind];
|
||||
}
|
||||
}
|
||||
|
||||
if ( !numIndexes ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
overlaySurface_t *s = (overlaySurface_t *) Mem_Alloc( sizeof( overlaySurface_t ) );
|
||||
s->surfaceNum = surfNum;
|
||||
s->surfaceId = surf->id;
|
||||
s->verts = (overlayVertex_t *)Mem_Alloc( numVerts * sizeof( s->verts[0] ) );
|
||||
memcpy( s->verts, overlayVerts, numVerts * sizeof( s->verts[0] ) );
|
||||
s->numVerts = numVerts;
|
||||
s->indexes = (glIndex_t *)Mem_Alloc( numIndexes * sizeof( s->indexes[0] ) );
|
||||
memcpy( s->indexes, overlayIndexes, numIndexes * sizeof( s->indexes[0] ) );
|
||||
s->numIndexes = numIndexes;
|
||||
|
||||
for ( i = 0; i < materials.Num(); i++ ) {
|
||||
if ( materials[i]->material == mtr ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i < materials.Num() ) {
|
||||
materials[i]->surfaces.Append( s );
|
||||
} else {
|
||||
overlayMaterial_t *mat = new overlayMaterial_t;
|
||||
mat->material = mtr;
|
||||
mat->surfaces.Append( s );
|
||||
materials.Append( mat );
|
||||
}
|
||||
}
|
||||
|
||||
// remove the oldest overlay surfaces if there are too many per material
|
||||
for ( i = 0; i < materials.Num(); i++ ) {
|
||||
while( materials[i]->surfaces.Num() > MAX_OVERLAY_SURFACES ) {
|
||||
FreeSurface( materials[i]->surfaces[0] );
|
||||
materials[i]->surfaces.RemoveIndex( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelOverlay::AddOverlaySurfacesToModel
|
||||
====================
|
||||
*/
|
||||
void idRenderModelOverlay::AddOverlaySurfacesToModel( idRenderModel *baseModel ) {
|
||||
int i, j, k, numVerts, numIndexes, surfaceNum;
|
||||
const modelSurface_t *baseSurf;
|
||||
idRenderModelStatic *staticModel;
|
||||
overlaySurface_t *surf;
|
||||
srfTriangles_t *newTri;
|
||||
modelSurface_t *newSurf;
|
||||
|
||||
if ( baseModel == NULL || baseModel->IsDefaultModel() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// md5 models won't have any surfaces when r_showSkel is set
|
||||
if ( !baseModel->NumSurfaces() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( baseModel->IsDynamicModel() != DM_STATIC ) {
|
||||
common->Error( "idRenderModelOverlay::AddOverlaySurfacesToModel: baseModel is not a static model" );
|
||||
}
|
||||
|
||||
assert( dynamic_cast<idRenderModelStatic *>(baseModel) != NULL );
|
||||
staticModel = static_cast<idRenderModelStatic *>(baseModel);
|
||||
|
||||
staticModel->overlaysAdded = 0;
|
||||
|
||||
if ( !materials.Num() ) {
|
||||
staticModel->DeleteSurfacesWithNegativeId();
|
||||
return;
|
||||
}
|
||||
|
||||
for ( k = 0; k < materials.Num(); k++ ) {
|
||||
|
||||
numVerts = numIndexes = 0;
|
||||
for ( i = 0; i < materials[k]->surfaces.Num(); i++ ) {
|
||||
numVerts += materials[k]->surfaces[i]->numVerts;
|
||||
numIndexes += materials[k]->surfaces[i]->numIndexes;
|
||||
}
|
||||
|
||||
if ( staticModel->FindSurfaceWithId( -1 - k, surfaceNum ) ) {
|
||||
newSurf = &staticModel->surfaces[surfaceNum];
|
||||
} else {
|
||||
newSurf = &staticModel->surfaces.Alloc();
|
||||
newSurf->geometry = NULL;
|
||||
newSurf->shader = materials[k]->material;
|
||||
newSurf->id = -1 - k;
|
||||
}
|
||||
|
||||
if ( newSurf->geometry == NULL || newSurf->geometry->numVerts < numVerts || newSurf->geometry->numIndexes < numIndexes ) {
|
||||
R_FreeStaticTriSurf( newSurf->geometry );
|
||||
newSurf->geometry = R_AllocStaticTriSurf();
|
||||
R_AllocStaticTriSurfVerts( newSurf->geometry, numVerts );
|
||||
R_AllocStaticTriSurfIndexes( newSurf->geometry, numIndexes );
|
||||
SIMDProcessor->Memset( newSurf->geometry->verts, 0, numVerts * sizeof( newTri->verts[0] ) );
|
||||
} else {
|
||||
R_FreeStaticTriSurfVertexCaches( newSurf->geometry );
|
||||
}
|
||||
|
||||
newTri = newSurf->geometry;
|
||||
numVerts = numIndexes = 0;
|
||||
|
||||
for ( i = 0; i < materials[k]->surfaces.Num(); i++ ) {
|
||||
surf = materials[k]->surfaces[i];
|
||||
|
||||
// get the model surface for this overlay surface
|
||||
if ( surf->surfaceNum < staticModel->NumSurfaces() ) {
|
||||
baseSurf = staticModel->Surface( surf->surfaceNum );
|
||||
} else {
|
||||
baseSurf = NULL;
|
||||
}
|
||||
|
||||
// if the surface ids no longer match
|
||||
if ( !baseSurf || baseSurf->id != surf->surfaceId ) {
|
||||
// find the surface with the correct id
|
||||
if ( staticModel->FindSurfaceWithId( surf->surfaceId, surf->surfaceNum ) ) {
|
||||
baseSurf = staticModel->Surface( surf->surfaceNum );
|
||||
} else {
|
||||
// the surface with this id no longer exists
|
||||
FreeSurface( surf );
|
||||
materials[k]->surfaces.RemoveIndex( i );
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// copy indexes;
|
||||
for ( j = 0; j < surf->numIndexes; j++ ) {
|
||||
newTri->indexes[numIndexes + j] = numVerts + surf->indexes[j];
|
||||
}
|
||||
numIndexes += surf->numIndexes;
|
||||
|
||||
// copy vertices
|
||||
for ( j = 0; j < surf->numVerts; j++ ) {
|
||||
overlayVertex_t *overlayVert = &surf->verts[j];
|
||||
|
||||
newTri->verts[numVerts].st[0] = overlayVert->st[0];
|
||||
newTri->verts[numVerts].st[1] = overlayVert->st[1];
|
||||
|
||||
if ( overlayVert->vertexNum >= baseSurf->geometry->numVerts ) {
|
||||
// This can happen when playing a demofile and a model has been changed since it was recorded, so just issue a warning and go on.
|
||||
common->Warning( "idRenderModelOverlay::AddOverlaySurfacesToModel: overlay vertex out of range. Model has probably changed since generating the overlay." );
|
||||
FreeSurface( surf );
|
||||
materials[k]->surfaces.RemoveIndex( i );
|
||||
staticModel->DeleteSurfaceWithId( newSurf->id );
|
||||
return;
|
||||
}
|
||||
newTri->verts[numVerts].xyz = baseSurf->geometry->verts[overlayVert->vertexNum].xyz;
|
||||
numVerts++;
|
||||
}
|
||||
}
|
||||
|
||||
newTri->numVerts = numVerts;
|
||||
newTri->numIndexes = numIndexes;
|
||||
R_BoundTriSurf( newTri );
|
||||
|
||||
staticModel->overlaysAdded++; // so we don't create an overlay on an overlay surface
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelOverlay::RemoveOverlaySurfacesFromModel
|
||||
====================
|
||||
*/
|
||||
void idRenderModelOverlay::RemoveOverlaySurfacesFromModel( idRenderModel *baseModel ) {
|
||||
idRenderModelStatic *staticModel;
|
||||
|
||||
assert( dynamic_cast<idRenderModelStatic *>(baseModel) != NULL );
|
||||
staticModel = static_cast<idRenderModelStatic *>(baseModel);
|
||||
|
||||
staticModel->DeleteSurfacesWithNegativeId();
|
||||
staticModel->overlaysAdded = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelOverlay::ReadFromDemoFile
|
||||
====================
|
||||
*/
|
||||
void idRenderModelOverlay::ReadFromDemoFile( idDemoFile *f ) {
|
||||
// FIXME: implement
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelOverlay::WriteToDemoFile
|
||||
====================
|
||||
*/
|
||||
void idRenderModelOverlay::WriteToDemoFile( idDemoFile *f ) const {
|
||||
// FIXME: implement
|
||||
}
|
||||
93
neo/renderer/ModelOverlay.h
Normal file
93
neo/renderer/ModelOverlay.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __MODELOVERLAY_H__
|
||||
#define __MODELOVERLAY_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Render model overlay for adding decals on top of dynamic models.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
const int MAX_OVERLAY_SURFACES = 16;
|
||||
|
||||
typedef struct overlayVertex_s {
|
||||
int vertexNum;
|
||||
float st[2];
|
||||
} overlayVertex_t;
|
||||
|
||||
typedef struct overlaySurface_s {
|
||||
int surfaceNum;
|
||||
int surfaceId;
|
||||
int numIndexes;
|
||||
glIndex_t * indexes;
|
||||
int numVerts;
|
||||
overlayVertex_t * verts;
|
||||
} overlaySurface_t;
|
||||
|
||||
typedef struct overlayMaterial_s {
|
||||
const idMaterial * material;
|
||||
idList<overlaySurface_t *> surfaces;
|
||||
} overlayMaterial_t;
|
||||
|
||||
|
||||
class idRenderModelOverlay {
|
||||
public:
|
||||
idRenderModelOverlay();
|
||||
~idRenderModelOverlay();
|
||||
|
||||
static idRenderModelOverlay *Alloc( void );
|
||||
static void Free( idRenderModelOverlay *overlay );
|
||||
|
||||
// Projects an overlay onto deformable geometry and can be added to
|
||||
// a render entity to allow decals on top of dynamic models.
|
||||
// This does not generate tangent vectors, so it can't be used with
|
||||
// light interaction shaders. Materials for overlays should always
|
||||
// be clamped, because the projected texcoords can run well off the
|
||||
// texture since no new clip vertexes are generated.
|
||||
void CreateOverlay( const idRenderModel *model, const idPlane localTextureAxis[2], const idMaterial *material );
|
||||
|
||||
// Creates new model surfaces for baseModel, which should be a static instantiation of a dynamic model.
|
||||
void AddOverlaySurfacesToModel( idRenderModel *baseModel );
|
||||
|
||||
// Removes overlay surfaces from the model.
|
||||
static void RemoveOverlaySurfacesFromModel( idRenderModel *baseModel );
|
||||
|
||||
void ReadFromDemoFile( class idDemoFile *f );
|
||||
void WriteToDemoFile( class idDemoFile *f ) const;
|
||||
|
||||
private:
|
||||
idList<overlayMaterial_t *> materials;
|
||||
|
||||
void FreeSurface( overlaySurface_t *surface );
|
||||
};
|
||||
|
||||
#endif /* !__MODELOVERLAY_H__ */
|
||||
912
neo/renderer/Model_ase.cpp
Normal file
912
neo/renderer/Model_ase.cpp
Normal file
@@ -0,0 +1,912 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "Model_ase.h"
|
||||
|
||||
/*
|
||||
======================================================================
|
||||
|
||||
Parses 3D Studio Max ASCII export files.
|
||||
The goal is to parse the information into memory exactly as it is
|
||||
represented in the file. Users of the data will then move it
|
||||
into a form that is more convenient for them.
|
||||
|
||||
======================================================================
|
||||
*/
|
||||
|
||||
|
||||
#define VERBOSE( x ) { if ( ase.verbose ) { common->Printf x ; } }
|
||||
|
||||
// working variables used during parsing
|
||||
typedef struct {
|
||||
const char *buffer;
|
||||
const char *curpos;
|
||||
int len;
|
||||
char token[1024];
|
||||
|
||||
bool verbose;
|
||||
|
||||
aseModel_t *model;
|
||||
aseObject_t *currentObject;
|
||||
aseMesh_t *currentMesh;
|
||||
aseMaterial_t *currentMaterial;
|
||||
int currentFace;
|
||||
int currentVertex;
|
||||
} ase_t;
|
||||
|
||||
static ase_t ase;
|
||||
|
||||
|
||||
static aseMesh_t *ASE_GetCurrentMesh( void )
|
||||
{
|
||||
return ase.currentMesh;
|
||||
}
|
||||
|
||||
static int CharIsTokenDelimiter( int ch )
|
||||
{
|
||||
if ( ch <= 32 )
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ASE_GetToken( bool restOfLine )
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if ( ase.buffer == 0 )
|
||||
return 0;
|
||||
|
||||
if ( ( ase.curpos - ase.buffer ) == ase.len )
|
||||
return 0;
|
||||
|
||||
// skip over crap
|
||||
while ( ( ( ase.curpos - ase.buffer ) < ase.len ) &&
|
||||
( *ase.curpos <= 32 ) )
|
||||
{
|
||||
ase.curpos++;
|
||||
}
|
||||
|
||||
while ( ( ase.curpos - ase.buffer ) < ase.len )
|
||||
{
|
||||
ase.token[i] = *ase.curpos;
|
||||
|
||||
ase.curpos++;
|
||||
i++;
|
||||
|
||||
if ( ( CharIsTokenDelimiter( ase.token[i-1] ) && !restOfLine ) ||
|
||||
( ( ase.token[i-1] == '\n' ) || ( ase.token[i-1] == '\r' ) ) )
|
||||
{
|
||||
ase.token[i-1] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ase.token[i] = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ASE_ParseBracedBlock( void (*parser)( const char *token ) )
|
||||
{
|
||||
int indent = 0;
|
||||
|
||||
while ( ASE_GetToken( false ) )
|
||||
{
|
||||
if ( !strcmp( ase.token, "{" ) )
|
||||
{
|
||||
indent++;
|
||||
}
|
||||
else if ( !strcmp( ase.token, "}" ) )
|
||||
{
|
||||
--indent;
|
||||
if ( indent == 0 )
|
||||
break;
|
||||
else if ( indent < 0 )
|
||||
common->Error( "Unexpected '}'" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( parser )
|
||||
parser( ase.token );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_SkipEnclosingBraces( void )
|
||||
{
|
||||
int indent = 0;
|
||||
|
||||
while ( ASE_GetToken( false ) )
|
||||
{
|
||||
if ( !strcmp( ase.token, "{" ) )
|
||||
{
|
||||
indent++;
|
||||
}
|
||||
else if ( !strcmp( ase.token, "}" ) )
|
||||
{
|
||||
indent--;
|
||||
if ( indent == 0 )
|
||||
break;
|
||||
else if ( indent < 0 )
|
||||
common->Error( "Unexpected '}'" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_SkipRestOfLine( void )
|
||||
{
|
||||
ASE_GetToken( true );
|
||||
}
|
||||
|
||||
static void ASE_KeyMAP_DIFFUSE( const char *token )
|
||||
{
|
||||
aseMaterial_t *material;
|
||||
|
||||
if ( !strcmp( token, "*BITMAP" ) )
|
||||
{
|
||||
idStr qpath;
|
||||
idStr matname;
|
||||
|
||||
ASE_GetToken( false );
|
||||
|
||||
// remove the quotes
|
||||
char *s = strstr( ase.token + 1, "\"" );
|
||||
if ( s ) {
|
||||
*s = 0;
|
||||
}
|
||||
matname = ase.token + 1;
|
||||
|
||||
// convert the 3DSMax material pathname to a qpath
|
||||
matname.BackSlashesToSlashes();
|
||||
qpath = fileSystem->OSPathToRelativePath( matname );
|
||||
idStr::Copynz( ase.currentMaterial->name, qpath, sizeof( ase.currentMaterial->name ) );
|
||||
}
|
||||
else if ( !strcmp( token, "*UVW_U_OFFSET" ) )
|
||||
{
|
||||
material = ase.model->materials[ase.model->materials.Num() - 1];
|
||||
ASE_GetToken( false );
|
||||
material->uOffset = atof( ase.token );
|
||||
}
|
||||
else if ( !strcmp( token, "*UVW_V_OFFSET" ) )
|
||||
{
|
||||
material = ase.model->materials[ase.model->materials.Num() - 1];
|
||||
ASE_GetToken( false );
|
||||
material->vOffset = atof( ase.token );
|
||||
}
|
||||
else if ( !strcmp( token, "*UVW_U_TILING" ) )
|
||||
{
|
||||
material = ase.model->materials[ase.model->materials.Num() - 1];
|
||||
ASE_GetToken( false );
|
||||
material->uTiling = atof( ase.token );
|
||||
}
|
||||
else if ( !strcmp( token, "*UVW_V_TILING" ) )
|
||||
{
|
||||
material = ase.model->materials[ase.model->materials.Num() - 1];
|
||||
ASE_GetToken( false );
|
||||
material->vTiling = atof( ase.token );
|
||||
}
|
||||
else if ( !strcmp( token, "*UVW_ANGLE" ) )
|
||||
{
|
||||
material = ase.model->materials[ase.model->materials.Num() - 1];
|
||||
ASE_GetToken( false );
|
||||
material->angle = atof( ase.token );
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyMATERIAL( const char *token )
|
||||
{
|
||||
if ( !strcmp( token, "*MAP_DIFFUSE" ) )
|
||||
{
|
||||
ASE_ParseBracedBlock( ASE_KeyMAP_DIFFUSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyMATERIAL_LIST( const char *token )
|
||||
{
|
||||
if ( !strcmp( token, "*MATERIAL_COUNT" ) )
|
||||
{
|
||||
ASE_GetToken( false );
|
||||
VERBOSE( ( "..num materials: %s\n", ase.token ) );
|
||||
}
|
||||
else if ( !strcmp( token, "*MATERIAL" ) )
|
||||
{
|
||||
VERBOSE( ( "..material %d\n", ase.model->materials.Num() ) );
|
||||
|
||||
ase.currentMaterial = (aseMaterial_t *)Mem_Alloc( sizeof( aseMaterial_t ) );
|
||||
memset( ase.currentMaterial, 0, sizeof( aseMaterial_t ) );
|
||||
ase.currentMaterial->uTiling = 1;
|
||||
ase.currentMaterial->vTiling = 1;
|
||||
ase.model->materials.Append(ase.currentMaterial);
|
||||
|
||||
ASE_ParseBracedBlock( ASE_KeyMATERIAL );
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyNODE_TM( const char *token )
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( !strcmp( token, "*TM_ROW0" ) ) {
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
ASE_GetToken( false );
|
||||
ase.currentObject->mesh.transform[0][i] = atof( ase.token );
|
||||
}
|
||||
} else if ( !strcmp( token, "*TM_ROW1" ) ) {
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
ASE_GetToken( false );
|
||||
ase.currentObject->mesh.transform[1][i] = atof( ase.token );
|
||||
}
|
||||
} else if ( !strcmp( token, "*TM_ROW2" ) ) {
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
ASE_GetToken( false );
|
||||
ase.currentObject->mesh.transform[2][i] = atof( ase.token );
|
||||
}
|
||||
} else if ( !strcmp( token, "*TM_ROW3" ) ) {
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
ASE_GetToken( false );
|
||||
ase.currentObject->mesh.transform[3][i] = atof( ase.token );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyMESH_VERTEX_LIST( const char *token )
|
||||
{
|
||||
aseMesh_t *pMesh = ASE_GetCurrentMesh();
|
||||
|
||||
if ( !strcmp( token, "*MESH_VERTEX" ) )
|
||||
{
|
||||
ASE_GetToken( false ); // skip number
|
||||
|
||||
ASE_GetToken( false );
|
||||
pMesh->vertexes[ase.currentVertex].x = atof( ase.token );
|
||||
|
||||
ASE_GetToken( false );
|
||||
pMesh->vertexes[ase.currentVertex].y = atof( ase.token );
|
||||
|
||||
ASE_GetToken( false );
|
||||
pMesh->vertexes[ase.currentVertex].z = atof( ase.token );
|
||||
|
||||
ase.currentVertex++;
|
||||
|
||||
if ( ase.currentVertex > pMesh->numVertexes )
|
||||
{
|
||||
common->Error( "ase.currentVertex >= pMesh->numVertexes" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
common->Error( "Unknown token '%s' while parsing MESH_VERTEX_LIST", token );
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyMESH_FACE_LIST( const char *token )
|
||||
{
|
||||
aseMesh_t *pMesh = ASE_GetCurrentMesh();
|
||||
|
||||
if ( !strcmp( token, "*MESH_FACE" ) )
|
||||
{
|
||||
ASE_GetToken( false ); // skip face number
|
||||
|
||||
// we are flipping the order here to change the front/back facing
|
||||
// from 3DS to our standard (clockwise facing out)
|
||||
ASE_GetToken( false ); // skip label
|
||||
ASE_GetToken( false ); // first vertex
|
||||
pMesh->faces[ase.currentFace].vertexNum[0] = atoi( ase.token );
|
||||
|
||||
ASE_GetToken( false ); // skip label
|
||||
ASE_GetToken( false ); // second vertex
|
||||
pMesh->faces[ase.currentFace].vertexNum[2] = atoi( ase.token );
|
||||
|
||||
ASE_GetToken( false ); // skip label
|
||||
ASE_GetToken( false ); // third vertex
|
||||
pMesh->faces[ase.currentFace].vertexNum[1] = atoi( ase.token );
|
||||
|
||||
ASE_GetToken( true );
|
||||
|
||||
// we could parse material id and smoothing groups here
|
||||
/*
|
||||
if ( ( p = strstr( ase.token, "*MESH_MTLID" ) ) != 0 )
|
||||
{
|
||||
p += strlen( "*MESH_MTLID" ) + 1;
|
||||
mtlID = atoi( p );
|
||||
}
|
||||
else
|
||||
{
|
||||
common->Error( "No *MESH_MTLID found for face!" );
|
||||
}
|
||||
*/
|
||||
|
||||
ase.currentFace++;
|
||||
}
|
||||
else
|
||||
{
|
||||
common->Error( "Unknown token '%s' while parsing MESH_FACE_LIST", token );
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyTFACE_LIST( const char *token )
|
||||
{
|
||||
aseMesh_t *pMesh = ASE_GetCurrentMesh();
|
||||
|
||||
if ( !strcmp( token, "*MESH_TFACE" ) )
|
||||
{
|
||||
int a, b, c;
|
||||
|
||||
ASE_GetToken( false );
|
||||
|
||||
ASE_GetToken( false );
|
||||
a = atoi( ase.token );
|
||||
ASE_GetToken( false );
|
||||
c = atoi( ase.token );
|
||||
ASE_GetToken( false );
|
||||
b = atoi( ase.token );
|
||||
|
||||
pMesh->faces[ase.currentFace].tVertexNum[0] = a;
|
||||
pMesh->faces[ase.currentFace].tVertexNum[1] = b;
|
||||
pMesh->faces[ase.currentFace].tVertexNum[2] = c;
|
||||
|
||||
ase.currentFace++;
|
||||
}
|
||||
else
|
||||
{
|
||||
common->Error( "Unknown token '%s' in MESH_TFACE", token );
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyCFACE_LIST( const char *token )
|
||||
{
|
||||
aseMesh_t *pMesh = ASE_GetCurrentMesh();
|
||||
|
||||
if ( !strcmp( token, "*MESH_CFACE" ) )
|
||||
{
|
||||
ASE_GetToken( false );
|
||||
|
||||
for ( int i = 0 ; i < 3 ; i++ ) {
|
||||
ASE_GetToken( false );
|
||||
int a = atoi( ase.token );
|
||||
|
||||
// we flip the vertex order to change the face direction to our style
|
||||
static int remap[3] = { 0, 2, 1 };
|
||||
pMesh->faces[ase.currentFace].vertexColors[remap[i]][0] = pMesh->cvertexes[a][0] * 255;
|
||||
pMesh->faces[ase.currentFace].vertexColors[remap[i]][1] = pMesh->cvertexes[a][1] * 255;
|
||||
pMesh->faces[ase.currentFace].vertexColors[remap[i]][2] = pMesh->cvertexes[a][2] * 255;
|
||||
}
|
||||
|
||||
ase.currentFace++;
|
||||
}
|
||||
else
|
||||
{
|
||||
common->Error( "Unknown token '%s' in MESH_CFACE", token );
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyMESH_TVERTLIST( const char *token )
|
||||
{
|
||||
aseMesh_t *pMesh = ASE_GetCurrentMesh();
|
||||
|
||||
if ( !strcmp( token, "*MESH_TVERT" ) )
|
||||
{
|
||||
char u[80], v[80], w[80];
|
||||
|
||||
ASE_GetToken( false );
|
||||
|
||||
ASE_GetToken( false );
|
||||
strcpy( u, ase.token );
|
||||
|
||||
ASE_GetToken( false );
|
||||
strcpy( v, ase.token );
|
||||
|
||||
ASE_GetToken( false );
|
||||
strcpy( w, ase.token );
|
||||
|
||||
pMesh->tvertexes[ase.currentVertex].x = atof( u );
|
||||
// our OpenGL second texture axis is inverted from MAX's sense
|
||||
pMesh->tvertexes[ase.currentVertex].y = 1.0f - atof( v );
|
||||
|
||||
ase.currentVertex++;
|
||||
|
||||
if ( ase.currentVertex > pMesh->numTVertexes )
|
||||
{
|
||||
common->Error( "ase.currentVertex > pMesh->numTVertexes" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
common->Error( "Unknown token '%s' while parsing MESH_TVERTLIST", token );
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyMESH_CVERTLIST( const char *token )
|
||||
{
|
||||
aseMesh_t *pMesh = ASE_GetCurrentMesh();
|
||||
|
||||
pMesh->colorsParsed = true;
|
||||
|
||||
if ( !strcmp( token, "*MESH_VERTCOL" ) )
|
||||
{
|
||||
ASE_GetToken( false );
|
||||
|
||||
ASE_GetToken( false );
|
||||
pMesh->cvertexes[ase.currentVertex][0] = atof( token );
|
||||
|
||||
ASE_GetToken( false );
|
||||
pMesh->cvertexes[ase.currentVertex][1] = atof( token );
|
||||
|
||||
ASE_GetToken( false );
|
||||
pMesh->cvertexes[ase.currentVertex][2] = atof( token );
|
||||
|
||||
ase.currentVertex++;
|
||||
|
||||
if ( ase.currentVertex > pMesh->numCVertexes )
|
||||
{
|
||||
common->Error( "ase.currentVertex > pMesh->numCVertexes" );
|
||||
}
|
||||
}
|
||||
else {
|
||||
common->Error( "Unknown token '%s' while parsing MESH_CVERTLIST", token );
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyMESH_NORMALS( const char *token )
|
||||
{
|
||||
aseMesh_t *pMesh = ASE_GetCurrentMesh();
|
||||
aseFace_t *f;
|
||||
idVec3 n;
|
||||
|
||||
pMesh->normalsParsed = true;
|
||||
f = &pMesh->faces[ase.currentFace];
|
||||
|
||||
if ( !strcmp( token, "*MESH_FACENORMAL" ) )
|
||||
{
|
||||
int num;
|
||||
|
||||
ASE_GetToken( false );
|
||||
num = atoi( ase.token );
|
||||
|
||||
if ( num >= pMesh->numFaces || num < 0 ) {
|
||||
common->Error( "MESH_NORMALS face index out of range: %i", num );
|
||||
}
|
||||
|
||||
if ( num != ase.currentFace ) {
|
||||
common->Error( "MESH_NORMALS face index != currentFace" );
|
||||
}
|
||||
|
||||
ASE_GetToken( false );
|
||||
n[0] = atof( ase.token );
|
||||
ASE_GetToken( false );
|
||||
n[1] = atof( ase.token );
|
||||
ASE_GetToken( false );
|
||||
n[2]= atof( ase.token );
|
||||
|
||||
f->faceNormal[0] = n[0] * pMesh->transform[0][0] + n[1] * pMesh->transform[1][0] + n[2] * pMesh->transform[2][0];
|
||||
f->faceNormal[1] = n[0] * pMesh->transform[0][1] + n[1] * pMesh->transform[1][1] + n[2] * pMesh->transform[2][1];
|
||||
f->faceNormal[2] = n[0] * pMesh->transform[0][2] + n[1] * pMesh->transform[1][2] + n[2] * pMesh->transform[2][2];
|
||||
|
||||
f->faceNormal.Normalize();
|
||||
|
||||
ase.currentFace++;
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_VERTEXNORMAL" ) )
|
||||
{
|
||||
int num;
|
||||
int v;
|
||||
|
||||
ASE_GetToken( false );
|
||||
num = atoi( ase.token );
|
||||
|
||||
if ( num >= pMesh->numVertexes || num < 0 ) {
|
||||
common->Error( "MESH_NORMALS vertex index out of range: %i", num );
|
||||
}
|
||||
|
||||
f = &pMesh->faces[ ase.currentFace - 1 ];
|
||||
|
||||
for ( v = 0 ; v < 3 ; v++ ) {
|
||||
if ( num == f->vertexNum[ v ] ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( v == 3 ) {
|
||||
common->Error( "MESH_NORMALS vertex index doesn't match face" );
|
||||
}
|
||||
|
||||
ASE_GetToken( false );
|
||||
n[0] = atof( ase.token );
|
||||
ASE_GetToken( false );
|
||||
n[1] = atof( ase.token );
|
||||
ASE_GetToken( false );
|
||||
n[2]= atof( ase.token );
|
||||
|
||||
f->vertexNormals[ v ][0] = n[0] * pMesh->transform[0][0] + n[1] * pMesh->transform[1][0] + n[2] * pMesh->transform[2][0];
|
||||
f->vertexNormals[ v ][1] = n[0] * pMesh->transform[0][1] + n[1] * pMesh->transform[1][1] + n[2] * pMesh->transform[2][1];
|
||||
f->vertexNormals[ v ][2] = n[0] * pMesh->transform[0][2] + n[1] * pMesh->transform[1][2] + n[2] * pMesh->transform[2][2];
|
||||
|
||||
f->vertexNormals[v].Normalize();
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyMESH( const char *token )
|
||||
{
|
||||
aseMesh_t *pMesh = ASE_GetCurrentMesh();
|
||||
|
||||
if ( !strcmp( token, "*TIMEVALUE" ) )
|
||||
{
|
||||
ASE_GetToken( false );
|
||||
|
||||
pMesh->timeValue = atoi( ase.token );
|
||||
VERBOSE( ( ".....timevalue: %d\n", pMesh->timeValue ) );
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_NUMVERTEX" ) )
|
||||
{
|
||||
ASE_GetToken( false );
|
||||
|
||||
pMesh->numVertexes = atoi( ase.token );
|
||||
VERBOSE( ( ".....num vertexes: %d\n", pMesh->numVertexes ) );
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_NUMTVERTEX" ) )
|
||||
{
|
||||
ASE_GetToken( false );
|
||||
|
||||
pMesh->numTVertexes = atoi( ase.token );
|
||||
VERBOSE( ( ".....num tvertexes: %d\n", pMesh->numTVertexes ) );
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_NUMCVERTEX" ) )
|
||||
{
|
||||
ASE_GetToken( false );
|
||||
|
||||
pMesh->numCVertexes = atoi( ase.token );
|
||||
VERBOSE( ( ".....num cvertexes: %d\n", pMesh->numCVertexes ) );
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_NUMFACES" ) )
|
||||
{
|
||||
ASE_GetToken( false );
|
||||
|
||||
pMesh->numFaces = atoi( ase.token );
|
||||
VERBOSE( ( ".....num faces: %d\n", pMesh->numFaces ) );
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_NUMTVFACES" ) )
|
||||
{
|
||||
ASE_GetToken( false );
|
||||
|
||||
pMesh->numTVFaces = atoi( ase.token );
|
||||
VERBOSE( ( ".....num tvfaces: %d\n", pMesh->numTVFaces ) );
|
||||
|
||||
if ( pMesh->numTVFaces != pMesh->numFaces )
|
||||
{
|
||||
common->Error( "MESH_NUMTVFACES != MESH_NUMFACES" );
|
||||
}
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_NUMCVFACES" ) )
|
||||
{
|
||||
ASE_GetToken( false );
|
||||
|
||||
pMesh->numCVFaces = atoi( ase.token );
|
||||
VERBOSE( ( ".....num cvfaces: %d\n", pMesh->numCVFaces ) );
|
||||
|
||||
if ( pMesh->numTVFaces != pMesh->numFaces )
|
||||
{
|
||||
common->Error( "MESH_NUMCVFACES != MESH_NUMFACES" );
|
||||
}
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_VERTEX_LIST" ) )
|
||||
{
|
||||
pMesh->vertexes = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numVertexes );
|
||||
ase.currentVertex = 0;
|
||||
VERBOSE( ( ".....parsing MESH_VERTEX_LIST\n" ) );
|
||||
ASE_ParseBracedBlock( ASE_KeyMESH_VERTEX_LIST );
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_TVERTLIST" ) )
|
||||
{
|
||||
ase.currentVertex = 0;
|
||||
pMesh->tvertexes = (idVec2 *)Mem_Alloc( sizeof( idVec2 ) * pMesh->numTVertexes );
|
||||
VERBOSE( ( ".....parsing MESH_TVERTLIST\n" ) );
|
||||
ASE_ParseBracedBlock( ASE_KeyMESH_TVERTLIST );
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_CVERTLIST" ) )
|
||||
{
|
||||
ase.currentVertex = 0;
|
||||
pMesh->cvertexes = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numCVertexes );
|
||||
VERBOSE( ( ".....parsing MESH_CVERTLIST\n" ) );
|
||||
ASE_ParseBracedBlock( ASE_KeyMESH_CVERTLIST );
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_FACE_LIST" ) )
|
||||
{
|
||||
pMesh->faces = (aseFace_t *)Mem_Alloc( sizeof( aseFace_t ) * pMesh->numFaces );
|
||||
ase.currentFace = 0;
|
||||
VERBOSE( ( ".....parsing MESH_FACE_LIST\n" ) );
|
||||
ASE_ParseBracedBlock( ASE_KeyMESH_FACE_LIST );
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_TFACELIST" ) )
|
||||
{
|
||||
if ( !pMesh->faces ) {
|
||||
common->Error( "*MESH_TFACELIST before *MESH_FACE_LIST" );
|
||||
}
|
||||
ase.currentFace = 0;
|
||||
VERBOSE( ( ".....parsing MESH_TFACE_LIST\n" ) );
|
||||
ASE_ParseBracedBlock( ASE_KeyTFACE_LIST );
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_CFACELIST" ) )
|
||||
{
|
||||
if ( !pMesh->faces ) {
|
||||
common->Error( "*MESH_CFACELIST before *MESH_FACE_LIST" );
|
||||
}
|
||||
ase.currentFace = 0;
|
||||
VERBOSE( ( ".....parsing MESH_CFACE_LIST\n" ) );
|
||||
ASE_ParseBracedBlock( ASE_KeyCFACE_LIST );
|
||||
}
|
||||
else if ( !strcmp( token, "*MESH_NORMALS" ) )
|
||||
{
|
||||
if ( !pMesh->faces ) {
|
||||
common->Warning( "*MESH_NORMALS before *MESH_FACE_LIST" );
|
||||
}
|
||||
ase.currentFace = 0;
|
||||
VERBOSE( ( ".....parsing MESH_NORMALS\n" ) );
|
||||
ASE_ParseBracedBlock( ASE_KeyMESH_NORMALS );
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyMESH_ANIMATION( const char *token )
|
||||
{
|
||||
aseMesh_t *mesh;
|
||||
|
||||
// loads a single animation frame
|
||||
if ( !strcmp( token, "*MESH" ) )
|
||||
{
|
||||
VERBOSE( ( "...found MESH\n" ) );
|
||||
|
||||
mesh = (aseMesh_t *)Mem_Alloc( sizeof( aseMesh_t ) );
|
||||
memset( mesh, 0, sizeof( aseMesh_t ) );
|
||||
ase.currentMesh = mesh;
|
||||
|
||||
ase.currentObject->frames.Append( mesh );
|
||||
|
||||
ASE_ParseBracedBlock( ASE_KeyMESH );
|
||||
}
|
||||
else
|
||||
{
|
||||
common->Error( "Unknown token '%s' while parsing MESH_ANIMATION", token );
|
||||
}
|
||||
}
|
||||
|
||||
static void ASE_KeyGEOMOBJECT( const char *token )
|
||||
{
|
||||
aseObject_t *object;
|
||||
|
||||
object = ase.currentObject;
|
||||
|
||||
if ( !strcmp( token, "*NODE_NAME" ) )
|
||||
{
|
||||
ASE_GetToken( true );
|
||||
VERBOSE( ( " %s\n", ase.token ) );
|
||||
idStr::Copynz( object->name, ase.token, sizeof( object->name ) );
|
||||
}
|
||||
else if ( !strcmp( token, "*NODE_PARENT" ) )
|
||||
{
|
||||
ASE_SkipRestOfLine();
|
||||
}
|
||||
// ignore unused data blocks
|
||||
else if ( !strcmp( token, "*NODE_TM" ) ||
|
||||
!strcmp( token, "*TM_ANIMATION" ) )
|
||||
{
|
||||
ASE_ParseBracedBlock( ASE_KeyNODE_TM );
|
||||
}
|
||||
// ignore regular meshes that aren't part of animation
|
||||
else if ( !strcmp( token, "*MESH" ) )
|
||||
{
|
||||
ase.currentMesh = &ase.currentObject->mesh;
|
||||
memset( ase.currentMesh, 0, sizeof( ase.currentMesh ) );
|
||||
|
||||
ASE_ParseBracedBlock( ASE_KeyMESH );
|
||||
}
|
||||
// according to spec these are obsolete
|
||||
else if ( !strcmp( token, "*MATERIAL_REF" ) )
|
||||
{
|
||||
ASE_GetToken( false );
|
||||
|
||||
object->materialRef = atoi( ase.token );
|
||||
}
|
||||
// loads a sequence of animation frames
|
||||
else if ( !strcmp( token, "*MESH_ANIMATION" ) )
|
||||
{
|
||||
VERBOSE( ( "..found MESH_ANIMATION\n" ) );
|
||||
|
||||
ASE_ParseBracedBlock( ASE_KeyMESH_ANIMATION );
|
||||
}
|
||||
// skip unused info
|
||||
else if ( !strcmp( token, "*PROP_MOTIONBLUR" ) ||
|
||||
!strcmp( token, "*PROP_CASTSHADOW" ) ||
|
||||
!strcmp( token, "*PROP_RECVSHADOW" ) )
|
||||
{
|
||||
ASE_SkipRestOfLine();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ASE_ParseGeomObject( void ) {
|
||||
aseObject_t *object;
|
||||
|
||||
VERBOSE( ("GEOMOBJECT" ) );
|
||||
|
||||
object = (aseObject_t *)Mem_Alloc( sizeof( aseObject_t ) );
|
||||
memset( object, 0, sizeof( aseObject_t ) );
|
||||
ase.model->objects.Append( object );
|
||||
ase.currentObject = object;
|
||||
|
||||
object->frames.Resize(32, 32);
|
||||
|
||||
ASE_ParseBracedBlock( ASE_KeyGEOMOBJECT );
|
||||
}
|
||||
|
||||
static void ASE_KeyGROUP( const char *token )
|
||||
{
|
||||
if ( !strcmp( token, "*GEOMOBJECT" ) ) {
|
||||
ASE_ParseGeomObject();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ASE_Parse
|
||||
=================
|
||||
*/
|
||||
aseModel_t *ASE_Parse( const char *buffer, bool verbose ) {
|
||||
memset( &ase, 0, sizeof( ase ) );
|
||||
|
||||
ase.verbose = verbose;
|
||||
|
||||
ase.buffer = buffer;
|
||||
ase.len = strlen( buffer );
|
||||
ase.curpos = ase.buffer;
|
||||
ase.currentObject = NULL;
|
||||
|
||||
// NOTE: using new operator because aseModel_t contains idList class objects
|
||||
ase.model = new aseModel_t;
|
||||
memset( ase.model, 0, sizeof( aseModel_t ) );
|
||||
ase.model->objects.Resize( 32, 32 );
|
||||
ase.model->materials.Resize( 32, 32 );
|
||||
|
||||
while ( ASE_GetToken( false ) ) {
|
||||
if ( !strcmp( ase.token, "*3DSMAX_ASCIIEXPORT" ) ||
|
||||
!strcmp( ase.token, "*COMMENT" ) ) {
|
||||
ASE_SkipRestOfLine();
|
||||
} else if ( !strcmp( ase.token, "*SCENE" ) ) {
|
||||
ASE_SkipEnclosingBraces();
|
||||
} else if ( !strcmp( ase.token, "*GROUP" ) ) {
|
||||
ASE_GetToken( false ); // group name
|
||||
ASE_ParseBracedBlock( ASE_KeyGROUP );
|
||||
} else if ( !strcmp( ase.token, "*SHAPEOBJECT" ) ) {
|
||||
ASE_SkipEnclosingBraces();
|
||||
} else if ( !strcmp( ase.token, "*CAMERAOBJECT" ) ) {
|
||||
ASE_SkipEnclosingBraces();
|
||||
} else if ( !strcmp( ase.token, "*MATERIAL_LIST" ) ) {
|
||||
VERBOSE( ("MATERIAL_LIST\n") );
|
||||
|
||||
ASE_ParseBracedBlock( ASE_KeyMATERIAL_LIST );
|
||||
} else if ( !strcmp( ase.token, "*GEOMOBJECT" ) ) {
|
||||
ASE_ParseGeomObject();
|
||||
} else if ( ase.token[0] ) {
|
||||
common->Printf( "Unknown token '%s'\n", ase.token );
|
||||
}
|
||||
}
|
||||
|
||||
return ase.model;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ASE_Load
|
||||
=================
|
||||
*/
|
||||
aseModel_t *ASE_Load( const char *fileName ) {
|
||||
char *buf;
|
||||
ID_TIME_T timeStamp;
|
||||
aseModel_t *ase;
|
||||
|
||||
fileSystem->ReadFile( fileName, (void **)&buf, &timeStamp );
|
||||
if ( !buf ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ase = ASE_Parse( buf, false );
|
||||
ase->timeStamp = timeStamp;
|
||||
|
||||
fileSystem->FreeFile( buf );
|
||||
|
||||
return ase;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ASE_Free
|
||||
=================
|
||||
*/
|
||||
void ASE_Free( aseModel_t *ase ) {
|
||||
int i, j;
|
||||
aseObject_t *obj;
|
||||
aseMesh_t *mesh;
|
||||
aseMaterial_t *material;
|
||||
|
||||
if ( !ase ) {
|
||||
return;
|
||||
}
|
||||
for ( i = 0; i < ase->objects.Num(); i++ ) {
|
||||
obj = ase->objects[i];
|
||||
for ( j = 0; j < obj->frames.Num(); j++ ) {
|
||||
mesh = obj->frames[j];
|
||||
if ( mesh->vertexes ) {
|
||||
Mem_Free( mesh->vertexes );
|
||||
}
|
||||
if ( mesh->tvertexes ) {
|
||||
Mem_Free( mesh->tvertexes );
|
||||
}
|
||||
if ( mesh->cvertexes ) {
|
||||
Mem_Free( mesh->cvertexes );
|
||||
}
|
||||
if ( mesh->faces ) {
|
||||
Mem_Free( mesh->faces );
|
||||
}
|
||||
Mem_Free( mesh );
|
||||
}
|
||||
|
||||
obj->frames.Clear();
|
||||
|
||||
// free the base nesh
|
||||
mesh = &obj->mesh;
|
||||
if ( mesh->vertexes ) {
|
||||
Mem_Free( mesh->vertexes );
|
||||
}
|
||||
if ( mesh->tvertexes ) {
|
||||
Mem_Free( mesh->tvertexes );
|
||||
}
|
||||
if ( mesh->cvertexes ) {
|
||||
Mem_Free( mesh->cvertexes );
|
||||
}
|
||||
if ( mesh->faces ) {
|
||||
Mem_Free( mesh->faces );
|
||||
}
|
||||
Mem_Free( obj );
|
||||
}
|
||||
ase->objects.Clear();
|
||||
|
||||
for ( i = 0; i < ase->materials.Num(); i++ ) {
|
||||
material = ase->materials[i];
|
||||
Mem_Free( material );
|
||||
}
|
||||
ase->materials.Clear();
|
||||
|
||||
delete ase;
|
||||
}
|
||||
95
neo/renderer/Model_ase.h
Normal file
95
neo/renderer/Model_ase.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __MODEL_ASE_H__
|
||||
#define __MODEL_ASE_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
ASE loader. (3D Studio Max ASCII Export)
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
int vertexNum[3];
|
||||
int tVertexNum[3];
|
||||
idVec3 faceNormal;
|
||||
idVec3 vertexNormals[3];
|
||||
byte vertexColors[3][4];
|
||||
} aseFace_t;
|
||||
|
||||
typedef struct {
|
||||
int timeValue;
|
||||
|
||||
int numVertexes;
|
||||
int numTVertexes;
|
||||
int numCVertexes;
|
||||
int numFaces;
|
||||
int numTVFaces;
|
||||
int numCVFaces;
|
||||
|
||||
idVec3 transform[4]; // applied to normals
|
||||
|
||||
bool colorsParsed;
|
||||
bool normalsParsed;
|
||||
idVec3 * vertexes;
|
||||
idVec2 * tvertexes;
|
||||
idVec3 * cvertexes;
|
||||
aseFace_t * faces;
|
||||
} aseMesh_t;
|
||||
|
||||
typedef struct {
|
||||
char name[128];
|
||||
float uOffset, vOffset; // max lets you offset by material without changing texCoords
|
||||
float uTiling, vTiling; // multiply tex coords by this
|
||||
float angle; // in clockwise radians
|
||||
} aseMaterial_t;
|
||||
|
||||
typedef struct {
|
||||
char name[128];
|
||||
int materialRef;
|
||||
|
||||
aseMesh_t mesh;
|
||||
|
||||
// frames are only present with animations
|
||||
idList<aseMesh_t*> frames; // aseMesh_t
|
||||
} aseObject_t;
|
||||
|
||||
typedef struct aseModel_s {
|
||||
ID_TIME_T timeStamp;
|
||||
idList<aseMaterial_t *> materials;
|
||||
idList<aseObject_t *> objects;
|
||||
} aseModel_t;
|
||||
|
||||
|
||||
aseModel_t *ASE_Load( const char *fileName );
|
||||
void ASE_Free( aseModel_t *ase );
|
||||
|
||||
#endif /* !__MODEL_ASE_H__ */
|
||||
213
neo/renderer/Model_beam.cpp
Normal file
213
neo/renderer/Model_beam.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
#include "Model_local.h"
|
||||
|
||||
/*
|
||||
|
||||
This is a simple dynamic model that just creates a stretched quad between
|
||||
two points that faces the view, like a dynamic deform tube.
|
||||
|
||||
*/
|
||||
|
||||
static const char *beam_SnapshotName = "_beam_Snapshot_";
|
||||
|
||||
/*
|
||||
===============
|
||||
idRenderModelBeam::IsDynamicModel
|
||||
===============
|
||||
*/
|
||||
dynamicModel_t idRenderModelBeam::IsDynamicModel() const {
|
||||
return DM_CONTINUOUS; // regenerate for every view
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
idRenderModelBeam::IsLoaded
|
||||
===============
|
||||
*/
|
||||
bool idRenderModelBeam::IsLoaded() const {
|
||||
return true; // don't ever need to load
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
idRenderModelBeam::InstantiateDynamicModel
|
||||
===============
|
||||
*/
|
||||
idRenderModel *idRenderModelBeam::InstantiateDynamicModel( const struct renderEntity_s *renderEntity, const struct viewDef_s *viewDef, idRenderModel *cachedModel ) {
|
||||
idRenderModelStatic *staticModel;
|
||||
srfTriangles_t *tri;
|
||||
modelSurface_t surf;
|
||||
|
||||
if ( cachedModel ) {
|
||||
delete cachedModel;
|
||||
cachedModel = NULL;
|
||||
}
|
||||
|
||||
if ( renderEntity == NULL || viewDef == NULL ) {
|
||||
delete cachedModel;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( cachedModel != NULL ) {
|
||||
|
||||
assert( dynamic_cast<idRenderModelStatic *>( cachedModel ) != NULL );
|
||||
assert( idStr::Icmp( cachedModel->Name(), beam_SnapshotName ) == 0 );
|
||||
|
||||
staticModel = static_cast<idRenderModelStatic *>( cachedModel );
|
||||
surf = *staticModel->Surface( 0 );
|
||||
tri = surf.geometry;
|
||||
|
||||
} else {
|
||||
|
||||
staticModel = new idRenderModelStatic;
|
||||
staticModel->InitEmpty( beam_SnapshotName );
|
||||
|
||||
tri = R_AllocStaticTriSurf();
|
||||
R_AllocStaticTriSurfVerts( tri, 4 );
|
||||
R_AllocStaticTriSurfIndexes( tri, 6 );
|
||||
|
||||
tri->verts[0].Clear();
|
||||
tri->verts[0].st[0] = 0;
|
||||
tri->verts[0].st[1] = 0;
|
||||
|
||||
tri->verts[1].Clear();
|
||||
tri->verts[1].st[0] = 0;
|
||||
tri->verts[1].st[1] = 1;
|
||||
|
||||
tri->verts[2].Clear();
|
||||
tri->verts[2].st[0] = 1;
|
||||
tri->verts[2].st[1] = 0;
|
||||
|
||||
tri->verts[3].Clear();
|
||||
tri->verts[3].st[0] = 1;
|
||||
tri->verts[3].st[1] = 1;
|
||||
|
||||
tri->indexes[0] = 0;
|
||||
tri->indexes[1] = 2;
|
||||
tri->indexes[2] = 1;
|
||||
tri->indexes[3] = 2;
|
||||
tri->indexes[4] = 3;
|
||||
tri->indexes[5] = 1;
|
||||
|
||||
tri->numVerts = 4;
|
||||
tri->numIndexes = 6;
|
||||
|
||||
surf.geometry = tri;
|
||||
surf.id = 0;
|
||||
surf.shader = tr.defaultMaterial;
|
||||
staticModel->AddSurface( surf );
|
||||
}
|
||||
|
||||
idVec3 target = *reinterpret_cast<const idVec3 *>( &renderEntity->shaderParms[SHADERPARM_BEAM_END_X] );
|
||||
|
||||
// we need the view direction to project the minor axis of the tube
|
||||
// as the view changes
|
||||
idVec3 localView, localTarget;
|
||||
float modelMatrix[16];
|
||||
R_AxisToModelMatrix( renderEntity->axis, renderEntity->origin, modelMatrix );
|
||||
R_GlobalPointToLocal( modelMatrix, viewDef->renderView.vieworg, localView );
|
||||
R_GlobalPointToLocal( modelMatrix, target, localTarget );
|
||||
|
||||
idVec3 major = localTarget;
|
||||
idVec3 minor;
|
||||
|
||||
idVec3 mid = 0.5f * localTarget;
|
||||
idVec3 dir = mid - localView;
|
||||
minor.Cross( major, dir );
|
||||
minor.Normalize();
|
||||
if ( renderEntity->shaderParms[SHADERPARM_BEAM_WIDTH] != 0.0f ) {
|
||||
minor *= renderEntity->shaderParms[SHADERPARM_BEAM_WIDTH] * 0.5f;
|
||||
}
|
||||
|
||||
int red = idMath::FtoiFast( renderEntity->shaderParms[SHADERPARM_RED] * 255.0f );
|
||||
int green = idMath::FtoiFast( renderEntity->shaderParms[SHADERPARM_GREEN] * 255.0f );
|
||||
int blue = idMath::FtoiFast( renderEntity->shaderParms[SHADERPARM_BLUE] * 255.0f );
|
||||
int alpha = idMath::FtoiFast( renderEntity->shaderParms[SHADERPARM_ALPHA] * 255.0f );
|
||||
|
||||
tri->verts[0].xyz = minor;
|
||||
tri->verts[0].color[0] = red;
|
||||
tri->verts[0].color[1] = green;
|
||||
tri->verts[0].color[2] = blue;
|
||||
tri->verts[0].color[3] = alpha;
|
||||
|
||||
tri->verts[1].xyz = -minor;
|
||||
tri->verts[1].color[0] = red;
|
||||
tri->verts[1].color[1] = green;
|
||||
tri->verts[1].color[2] = blue;
|
||||
tri->verts[1].color[3] = alpha;
|
||||
|
||||
tri->verts[2].xyz = localTarget + minor;
|
||||
tri->verts[2].color[0] = red;
|
||||
tri->verts[2].color[1] = green;
|
||||
tri->verts[2].color[2] = blue;
|
||||
tri->verts[2].color[3] = alpha;
|
||||
|
||||
tri->verts[3].xyz = localTarget - minor;
|
||||
tri->verts[3].color[0] = red;
|
||||
tri->verts[3].color[1] = green;
|
||||
tri->verts[3].color[2] = blue;
|
||||
tri->verts[3].color[3] = alpha;
|
||||
|
||||
R_BoundTriSurf( tri );
|
||||
|
||||
staticModel->bounds = tri->bounds;
|
||||
|
||||
return staticModel;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
idRenderModelBeam::Bounds
|
||||
===============
|
||||
*/
|
||||
idBounds idRenderModelBeam::Bounds( const struct renderEntity_s *renderEntity ) const {
|
||||
idBounds b;
|
||||
|
||||
b.Zero();
|
||||
if ( !renderEntity ) {
|
||||
b.ExpandSelf( 8.0f );
|
||||
} else {
|
||||
idVec3 target = *reinterpret_cast<const idVec3 *>( &renderEntity->shaderParms[SHADERPARM_BEAM_END_X] );
|
||||
idVec3 localTarget;
|
||||
float modelMatrix[16];
|
||||
R_AxisToModelMatrix( renderEntity->axis, renderEntity->origin, modelMatrix );
|
||||
R_GlobalPointToLocal( modelMatrix, target, localTarget );
|
||||
|
||||
b.AddPoint( localTarget );
|
||||
if ( renderEntity->shaderParms[SHADERPARM_BEAM_WIDTH] != 0.0f ) {
|
||||
b.ExpandSelf( renderEntity->shaderParms[SHADERPARM_BEAM_WIDTH] * 0.5f );
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
532
neo/renderer/Model_liquid.cpp
Normal file
532
neo/renderer/Model_liquid.cpp
Normal file
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
#include "Model_local.h"
|
||||
|
||||
#define LIQUID_MAX_SKIP_FRAMES 5
|
||||
#define LIQUID_MAX_TYPES 3
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelLiquid::idRenderModelLiquid
|
||||
====================
|
||||
*/
|
||||
idRenderModelLiquid::idRenderModelLiquid() {
|
||||
verts_x = 32;
|
||||
verts_y = 32;
|
||||
scale_x = 256.0f;
|
||||
scale_y = 256.0f;
|
||||
liquid_type = 0;
|
||||
density = 0.97f;
|
||||
drop_height = 4;
|
||||
drop_radius = 4;
|
||||
drop_delay = 1000;
|
||||
shader = declManager->FindMaterial( NULL );
|
||||
update_tics = 33; // ~30 hz
|
||||
time = 0;
|
||||
seed = 0;
|
||||
|
||||
random.SetSeed( 0 );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelLiquid::GenerateSurface
|
||||
====================
|
||||
*/
|
||||
modelSurface_t idRenderModelLiquid::GenerateSurface( float lerp ) {
|
||||
srfTriangles_t *tri;
|
||||
int i, base;
|
||||
idDrawVert *vert;
|
||||
modelSurface_t surf;
|
||||
float inv_lerp;
|
||||
|
||||
inv_lerp = 1.0f - lerp;
|
||||
vert = verts.Ptr();
|
||||
for( i = 0; i < verts.Num(); i++, vert++ ) {
|
||||
vert->xyz.z = page1[ i ] * lerp + page2[ i ] * inv_lerp;
|
||||
}
|
||||
|
||||
tr.pc.c_deformedSurfaces++;
|
||||
tr.pc.c_deformedVerts += deformInfo->numOutputVerts;
|
||||
tr.pc.c_deformedIndexes += deformInfo->numIndexes;
|
||||
|
||||
tri = R_AllocStaticTriSurf();
|
||||
|
||||
// note that some of the data is references, and should not be freed
|
||||
tri->deformedSurface = true;
|
||||
|
||||
tri->numIndexes = deformInfo->numIndexes;
|
||||
tri->indexes = deformInfo->indexes;
|
||||
tri->silIndexes = deformInfo->silIndexes;
|
||||
tri->numMirroredVerts = deformInfo->numMirroredVerts;
|
||||
tri->mirroredVerts = deformInfo->mirroredVerts;
|
||||
tri->numDupVerts = deformInfo->numDupVerts;
|
||||
tri->dupVerts = deformInfo->dupVerts;
|
||||
tri->numSilEdges = deformInfo->numSilEdges;
|
||||
tri->silEdges = deformInfo->silEdges;
|
||||
tri->dominantTris = deformInfo->dominantTris;
|
||||
|
||||
tri->numVerts = deformInfo->numOutputVerts;
|
||||
R_AllocStaticTriSurfVerts( tri, tri->numVerts );
|
||||
SIMDProcessor->Memcpy( tri->verts, verts.Ptr(), deformInfo->numSourceVerts * sizeof(tri->verts[0]) );
|
||||
|
||||
// replicate the mirror seam vertexes
|
||||
base = deformInfo->numOutputVerts - deformInfo->numMirroredVerts;
|
||||
for ( i = 0 ; i < deformInfo->numMirroredVerts ; i++ ) {
|
||||
tri->verts[base + i] = tri->verts[deformInfo->mirroredVerts[i]];
|
||||
}
|
||||
|
||||
R_BoundTriSurf( tri );
|
||||
|
||||
// If a surface is going to be have a lighting interaction generated, it will also have to call
|
||||
// R_DeriveTangents() to get normals, tangents, and face planes. If it only
|
||||
// needs shadows generated, it will only have to generate face planes. If it only
|
||||
// has ambient drawing, or is culled, no additional work will be necessary
|
||||
if ( !r_useDeferredTangents.GetBool() ) {
|
||||
// set face planes, vertex normals, tangents
|
||||
R_DeriveTangents( tri );
|
||||
}
|
||||
|
||||
surf.geometry = tri;
|
||||
surf.shader = shader;
|
||||
|
||||
return surf;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelLiquid::WaterDrop
|
||||
====================
|
||||
*/
|
||||
void idRenderModelLiquid::WaterDrop( int x, int y, float *page ) {
|
||||
int cx, cy;
|
||||
int left,top,right,bottom;
|
||||
int square;
|
||||
int radsquare = drop_radius * drop_radius;
|
||||
float invlength = 1.0f / ( float )radsquare;
|
||||
float dist;
|
||||
|
||||
if ( x < 0 ) {
|
||||
x = 1 + drop_radius + random.RandomInt( verts_x - 2 * drop_radius - 1 );
|
||||
}
|
||||
if ( y < 0 ) {
|
||||
y = 1 + drop_radius + random.RandomInt( verts_y - 2 * drop_radius - 1 );
|
||||
}
|
||||
|
||||
left=-drop_radius; right = drop_radius;
|
||||
top=-drop_radius; bottom = drop_radius;
|
||||
|
||||
// Perform edge clipping...
|
||||
if ( x - drop_radius < 1 ) {
|
||||
left -= (x-drop_radius-1);
|
||||
}
|
||||
if ( y - drop_radius < 1 ) {
|
||||
top -= (y-drop_radius-1);
|
||||
}
|
||||
if ( x + drop_radius > verts_x - 1 ) {
|
||||
right -= (x+drop_radius-verts_x+1);
|
||||
}
|
||||
if ( y + drop_radius > verts_y - 1 ) {
|
||||
bottom-= (y+drop_radius-verts_y+1);
|
||||
}
|
||||
|
||||
for ( cy = top; cy < bottom; cy++ ) {
|
||||
for ( cx = left; cx < right; cx++ ) {
|
||||
square = cy*cy + cx*cx;
|
||||
if ( square < radsquare ) {
|
||||
dist = idMath::Sqrt( (float)square * invlength );
|
||||
page[verts_x*(cy+y) + cx+x] += idMath::Cos16( dist * idMath::PI * 0.5f ) * drop_height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelLiquid::IntersectBounds
|
||||
====================
|
||||
*/
|
||||
void idRenderModelLiquid::IntersectBounds( const idBounds &bounds, float displacement ) {
|
||||
int cx, cy;
|
||||
int left,top,right,bottom;
|
||||
float up, down;
|
||||
float *pos;
|
||||
|
||||
left = ( int )( bounds[ 0 ].x / scale_x );
|
||||
right = ( int )( bounds[ 1 ].x / scale_x );
|
||||
top = ( int )( bounds[ 0 ].y / scale_y );
|
||||
bottom = ( int )( bounds[ 1 ].y / scale_y );
|
||||
down = bounds[ 0 ].z;
|
||||
up = bounds[ 1 ].z;
|
||||
|
||||
if ( ( right < 1 ) || ( left >= verts_x ) || ( bottom < 1 ) || ( top >= verts_x ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform edge clipping...
|
||||
if ( left < 1 ) {
|
||||
left = 1;
|
||||
}
|
||||
if ( right >= verts_x ) {
|
||||
right = verts_x - 1;
|
||||
}
|
||||
if ( top < 1 ) {
|
||||
top = 1;
|
||||
}
|
||||
if ( bottom >= verts_y ) {
|
||||
bottom = verts_y - 1;
|
||||
}
|
||||
|
||||
for ( cy = top; cy < bottom; cy++ ) {
|
||||
for ( cx = left; cx < right; cx++ ) {
|
||||
pos = &page1[ verts_x * cy + cx ];
|
||||
if ( *pos > down ) {//&& ( *pos < up ) ) {
|
||||
*pos = down;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelLiquid::Update
|
||||
====================
|
||||
*/
|
||||
void idRenderModelLiquid::Update( void ) {
|
||||
int x, y;
|
||||
float *p2;
|
||||
float *p1;
|
||||
float value;
|
||||
|
||||
time += update_tics;
|
||||
|
||||
idSwap( page1, page2 );
|
||||
|
||||
if ( time > nextDropTime ) {
|
||||
WaterDrop( -1, -1, page2 );
|
||||
nextDropTime = time + drop_delay;
|
||||
} else if ( time < nextDropTime - drop_delay ) {
|
||||
nextDropTime = time + drop_delay;
|
||||
}
|
||||
|
||||
p1 = page1;
|
||||
p2 = page2;
|
||||
|
||||
switch( liquid_type ) {
|
||||
case 0 :
|
||||
for ( y = 1; y < verts_y - 1; y++ ) {
|
||||
p2 += verts_x;
|
||||
p1 += verts_x;
|
||||
for ( x = 1; x < verts_x - 1; x++ ) {
|
||||
value =
|
||||
( p2[ x + verts_x ] +
|
||||
p2[ x - verts_x ] +
|
||||
p2[ x + 1 ] +
|
||||
p2[ x - 1 ] +
|
||||
p2[ x - verts_x - 1 ] +
|
||||
p2[ x - verts_x + 1 ] +
|
||||
p2[ x + verts_x - 1 ] +
|
||||
p2[ x + verts_x + 1 ] +
|
||||
p2[ x ] ) * ( 2.0f / 9.0f ) -
|
||||
p1[ x ];
|
||||
|
||||
p1[ x ] = value * density;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 1 :
|
||||
for ( y = 1; y < verts_y - 1; y++ ) {
|
||||
p2 += verts_x;
|
||||
p1 += verts_x;
|
||||
for ( x = 1; x < verts_x - 1; x++ ) {
|
||||
value =
|
||||
( p2[ x + verts_x ] +
|
||||
p2[ x - verts_x ] +
|
||||
p2[ x + 1 ] +
|
||||
p2[ x - 1 ] +
|
||||
p2[ x - verts_x - 1 ] +
|
||||
p2[ x - verts_x + 1 ] +
|
||||
p2[ x + verts_x - 1 ] +
|
||||
p2[ x + verts_x + 1 ] ) * 0.25f -
|
||||
p1[ x ];
|
||||
|
||||
p1[ x ] = value * density;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2 :
|
||||
for ( y = 1; y < verts_y - 1; y++ ) {
|
||||
p2 += verts_x;
|
||||
p1 += verts_x;
|
||||
for ( x = 1; x < verts_x - 1; x++ ) {
|
||||
value =
|
||||
( p2[ x + verts_x ] +
|
||||
p2[ x - verts_x ] +
|
||||
p2[ x + 1 ] +
|
||||
p2[ x - 1 ] +
|
||||
p2[ x - verts_x - 1 ] +
|
||||
p2[ x - verts_x + 1 ] +
|
||||
p2[ x + verts_x - 1 ] +
|
||||
p2[ x + verts_x + 1 ] +
|
||||
p2[ x ] ) * ( 1.0f / 9.0f );
|
||||
|
||||
p1[ x ] = value * density;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelLiquid::Reset
|
||||
====================
|
||||
*/
|
||||
void idRenderModelLiquid::Reset() {
|
||||
int i, x, y;
|
||||
|
||||
if ( pages.Num() < 2 * verts_x * verts_y ) {
|
||||
return;
|
||||
}
|
||||
|
||||
nextDropTime = 0;
|
||||
time = 0;
|
||||
random.SetSeed( seed );
|
||||
|
||||
page1 = pages.Ptr();
|
||||
page2 = page1 + verts_x * verts_y;
|
||||
|
||||
for ( i = 0, y = 0; y < verts_y; y++ ) {
|
||||
for ( x = 0; x < verts_x; x++, i++ ) {
|
||||
page1[ i ] = 0.0f;
|
||||
page2[ i ] = 0.0f;
|
||||
verts[ i ].xyz.z = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelLiquid::InitFromFile
|
||||
====================
|
||||
*/
|
||||
void idRenderModelLiquid::InitFromFile( const char *fileName ) {
|
||||
int i, x, y;
|
||||
idToken token;
|
||||
idParser parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS );
|
||||
idList<int> tris;
|
||||
float size_x, size_y;
|
||||
float rate;
|
||||
|
||||
name = fileName;
|
||||
|
||||
if ( !parser.LoadFile( fileName ) ) {
|
||||
MakeDefaultModel();
|
||||
return;
|
||||
}
|
||||
|
||||
size_x = scale_x * verts_x;
|
||||
size_y = scale_y * verts_y;
|
||||
|
||||
while( parser.ReadToken( &token ) ) {
|
||||
if ( !token.Icmp( "seed" ) ) {
|
||||
seed = parser.ParseInt();
|
||||
} else if ( !token.Icmp( "size_x" ) ) {
|
||||
size_x = parser.ParseFloat();
|
||||
} else if ( !token.Icmp( "size_y" ) ) {
|
||||
size_y = parser.ParseFloat();
|
||||
} else if ( !token.Icmp( "verts_x" ) ) {
|
||||
verts_x = parser.ParseFloat();
|
||||
if ( verts_x < 2 ) {
|
||||
parser.Warning( "Invalid # of verts. Using default model." );
|
||||
MakeDefaultModel();
|
||||
return;
|
||||
}
|
||||
} else if ( !token.Icmp( "verts_y" ) ) {
|
||||
verts_y = parser.ParseFloat();
|
||||
if ( verts_y < 2 ) {
|
||||
parser.Warning( "Invalid # of verts. Using default model." );
|
||||
MakeDefaultModel();
|
||||
return;
|
||||
}
|
||||
} else if ( !token.Icmp( "liquid_type" ) ) {
|
||||
liquid_type = parser.ParseInt() - 1;
|
||||
if ( ( liquid_type < 0 ) || ( liquid_type >= LIQUID_MAX_TYPES ) ) {
|
||||
parser.Warning( "Invalid liquid_type. Using default model." );
|
||||
MakeDefaultModel();
|
||||
return;
|
||||
}
|
||||
} else if ( !token.Icmp( "density" ) ) {
|
||||
density = parser.ParseFloat();
|
||||
} else if ( !token.Icmp( "drop_height" ) ) {
|
||||
drop_height = parser.ParseFloat();
|
||||
} else if ( !token.Icmp( "drop_radius" ) ) {
|
||||
drop_radius = parser.ParseInt();
|
||||
} else if ( !token.Icmp( "drop_delay" ) ) {
|
||||
drop_delay = SEC2MS( parser.ParseFloat() );
|
||||
} else if ( !token.Icmp( "shader" ) ) {
|
||||
parser.ReadToken( &token );
|
||||
shader = declManager->FindMaterial( token );
|
||||
} else if ( !token.Icmp( "seed" ) ) {
|
||||
seed = parser.ParseInt();
|
||||
} else if ( !token.Icmp( "update_rate" ) ) {
|
||||
rate = parser.ParseFloat();
|
||||
if ( ( rate <= 0.0f ) || ( rate > 60.0f ) ) {
|
||||
parser.Warning( "Invalid update_rate. Must be between 0 and 60. Using default model." );
|
||||
MakeDefaultModel();
|
||||
return;
|
||||
}
|
||||
update_tics = 1000 / rate;
|
||||
} else {
|
||||
parser.Warning( "Unknown parameter '%s'. Using default model.", token.c_str() );
|
||||
MakeDefaultModel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
scale_x = size_x / ( verts_x - 1 );
|
||||
scale_y = size_y / ( verts_y - 1 );
|
||||
|
||||
pages.SetNum( 2 * verts_x * verts_y );
|
||||
page1 = pages.Ptr();
|
||||
page2 = page1 + verts_x * verts_y;
|
||||
|
||||
verts.SetNum( verts_x * verts_y );
|
||||
for ( i = 0, y = 0; y < verts_y; y++ ) {
|
||||
for ( x = 0; x < verts_x; x++, i++ ) {
|
||||
page1[ i ] = 0.0f;
|
||||
page2[ i ] = 0.0f;
|
||||
verts[ i ].Clear();
|
||||
verts[ i ].xyz.Set( x * scale_x, y * scale_y, 0.0f );
|
||||
verts[ i ].st.Set( (float) x / (float)( verts_x - 1 ), (float) -y / (float)( verts_y - 1 ) );
|
||||
}
|
||||
}
|
||||
|
||||
tris.SetNum( ( verts_x - 1 ) * ( verts_y - 1 ) * 6 );
|
||||
for( i = 0, y = 0; y < verts_y - 1; y++ ) {
|
||||
for( x = 1; x < verts_x; x++, i += 6 ) {
|
||||
tris[ i + 0 ] = y * verts_x + x;
|
||||
tris[ i + 1 ] = y * verts_x + x - 1;
|
||||
tris[ i + 2 ] = ( y + 1 ) * verts_x + x - 1;
|
||||
|
||||
tris[ i + 3 ] = ( y + 1 ) * verts_x + x - 1;
|
||||
tris[ i + 4 ] = ( y + 1 ) * verts_x + x;
|
||||
tris[ i + 5 ] = y * verts_x + x;
|
||||
}
|
||||
}
|
||||
|
||||
// build the information that will be common to all animations of this mesh:
|
||||
// sil edge connectivity and normal / tangent generation information
|
||||
deformInfo = R_BuildDeformInfo( verts.Num(), verts.Ptr(), tris.Num(), tris.Ptr(), true );
|
||||
|
||||
bounds.Clear();
|
||||
bounds.AddPoint( idVec3( 0.0f, 0.0f, drop_height * -10.0f ) );
|
||||
bounds.AddPoint( idVec3( ( verts_x - 1 ) * scale_x, ( verts_y - 1 ) * scale_y, drop_height * 10.0f ) );
|
||||
|
||||
// set the timestamp for reloadmodels
|
||||
fileSystem->ReadFile( name, NULL, &timeStamp );
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelLiquid::InstantiateDynamicModel
|
||||
====================
|
||||
*/
|
||||
idRenderModel *idRenderModelLiquid::InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) {
|
||||
idRenderModelStatic *staticModel;
|
||||
int frames;
|
||||
int t;
|
||||
float lerp;
|
||||
|
||||
if ( cachedModel ) {
|
||||
delete cachedModel;
|
||||
cachedModel = NULL;
|
||||
}
|
||||
|
||||
if ( !deformInfo ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( !view ) {
|
||||
t = 0;
|
||||
} else {
|
||||
t = view->renderView.time;
|
||||
}
|
||||
|
||||
// update the liquid model
|
||||
frames = ( t - time ) / update_tics;
|
||||
if ( frames > LIQUID_MAX_SKIP_FRAMES ) {
|
||||
// don't let time accumalate when skipping frames
|
||||
time += update_tics * ( frames - LIQUID_MAX_SKIP_FRAMES );
|
||||
|
||||
frames = LIQUID_MAX_SKIP_FRAMES;
|
||||
}
|
||||
|
||||
while( frames > 0 ) {
|
||||
Update();
|
||||
frames--;
|
||||
}
|
||||
|
||||
// create the surface
|
||||
lerp = ( float )( t - time ) / ( float )update_tics;
|
||||
modelSurface_t surf = GenerateSurface( lerp );
|
||||
|
||||
staticModel = new idRenderModelStatic;
|
||||
staticModel->AddSurface( surf );
|
||||
staticModel->bounds = surf.geometry->bounds;
|
||||
|
||||
return staticModel;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelLiquid::IsDynamicModel
|
||||
====================
|
||||
*/
|
||||
dynamicModel_t idRenderModelLiquid::IsDynamicModel() const {
|
||||
return DM_CONTINUOUS;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelLiquid::Bounds
|
||||
====================
|
||||
*/
|
||||
idBounds idRenderModelLiquid::Bounds(const struct renderEntity_s *ent) const {
|
||||
// FIXME: need to do this better
|
||||
return bounds;
|
||||
}
|
||||
385
neo/renderer/Model_local.h
Normal file
385
neo/renderer/Model_local.h
Normal file
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __MODEL_LOCAL_H__
|
||||
#define __MODEL_LOCAL_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Static model
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idRenderModelStatic : public idRenderModel {
|
||||
public:
|
||||
// the inherited public interface
|
||||
static idRenderModel * Alloc();
|
||||
|
||||
idRenderModelStatic();
|
||||
virtual ~idRenderModelStatic();
|
||||
|
||||
virtual void InitFromFile( const char *fileName );
|
||||
virtual void PartialInitFromFile( const char *fileName );
|
||||
virtual void PurgeModel();
|
||||
virtual void Reset() {};
|
||||
virtual void LoadModel();
|
||||
virtual bool IsLoaded();
|
||||
virtual void SetLevelLoadReferenced( bool referenced );
|
||||
virtual bool IsLevelLoadReferenced();
|
||||
virtual void TouchData();
|
||||
virtual void InitEmpty( const char *name );
|
||||
virtual void AddSurface( modelSurface_t surface );
|
||||
virtual void FinishSurfaces();
|
||||
virtual void FreeVertexCache();
|
||||
virtual const char * Name() const;
|
||||
virtual void Print() const;
|
||||
virtual void List() const;
|
||||
virtual int Memory() const;
|
||||
virtual ID_TIME_T Timestamp() const;
|
||||
virtual int NumSurfaces() const;
|
||||
virtual int NumBaseSurfaces() const;
|
||||
virtual const modelSurface_t *Surface( int surfaceNum ) const;
|
||||
virtual srfTriangles_t * AllocSurfaceTriangles( int numVerts, int numIndexes ) const;
|
||||
virtual void FreeSurfaceTriangles( srfTriangles_t *tris ) const;
|
||||
virtual srfTriangles_t * ShadowHull() const;
|
||||
virtual bool IsStaticWorldModel() const;
|
||||
virtual dynamicModel_t IsDynamicModel() const;
|
||||
virtual bool IsDefaultModel() const;
|
||||
virtual bool IsReloadable() const;
|
||||
virtual idRenderModel * InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel );
|
||||
virtual int NumJoints( void ) const;
|
||||
virtual const idMD5Joint * GetJoints( void ) const;
|
||||
virtual jointHandle_t GetJointHandle( const char *name ) const;
|
||||
virtual const char * GetJointName( jointHandle_t handle ) const;
|
||||
virtual const idJointQuat * GetDefaultPose( void ) const;
|
||||
virtual int NearestJoint( int surfaceNum, int a, int b, int c ) const;
|
||||
virtual idBounds Bounds( const struct renderEntity_s *ent ) const;
|
||||
virtual void ReadFromDemoFile( class idDemoFile *f );
|
||||
virtual void WriteToDemoFile( class idDemoFile *f );
|
||||
virtual float DepthHack() const;
|
||||
|
||||
void MakeDefaultModel();
|
||||
|
||||
bool LoadASE( const char *fileName );
|
||||
bool LoadLWO( const char *fileName );
|
||||
bool LoadFLT( const char *fileName );
|
||||
bool LoadMA( const char *filename );
|
||||
|
||||
bool ConvertASEToModelSurfaces( const struct aseModel_s *ase );
|
||||
bool ConvertLWOToModelSurfaces( const struct st_lwObject *lwo );
|
||||
bool ConvertMAToModelSurfaces (const struct maModel_s *ma );
|
||||
|
||||
struct aseModel_s * ConvertLWOToASE( const struct st_lwObject *obj, const char *fileName );
|
||||
|
||||
bool DeleteSurfaceWithId( int id );
|
||||
void DeleteSurfacesWithNegativeId( void );
|
||||
bool FindSurfaceWithId( int id, int &surfaceNum );
|
||||
|
||||
public:
|
||||
idList<modelSurface_t> surfaces;
|
||||
idBounds bounds;
|
||||
int overlaysAdded;
|
||||
|
||||
protected:
|
||||
int lastModifiedFrame;
|
||||
int lastArchivedFrame;
|
||||
|
||||
idStr name;
|
||||
srfTriangles_t * shadowHull;
|
||||
bool isStaticWorldModel;
|
||||
bool defaulted;
|
||||
bool purged; // eventually we will have dynamic reloading
|
||||
bool fastLoad; // don't generate tangents and shadow data
|
||||
bool reloadable; // if not, reloadModels won't check timestamp
|
||||
bool levelLoadReferenced; // for determining if it needs to be freed
|
||||
ID_TIME_T timeStamp;
|
||||
|
||||
static idCVar r_mergeModelSurfaces; // combine model surfaces with the same material
|
||||
static idCVar r_slopVertex; // merge xyz coordinates this far apart
|
||||
static idCVar r_slopTexCoord; // merge texture coordinates this far apart
|
||||
static idCVar r_slopNormal; // merge normals that dot less than this
|
||||
};
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
MD5 animated model
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idMD5Mesh {
|
||||
friend class idRenderModelMD5;
|
||||
|
||||
public:
|
||||
idMD5Mesh();
|
||||
~idMD5Mesh();
|
||||
|
||||
void ParseMesh( idLexer &parser, int numJoints, const idJointMat *joints );
|
||||
void UpdateSurface( const struct renderEntity_s *ent, const idJointMat *joints, modelSurface_t *surf );
|
||||
idBounds CalcBounds( const idJointMat *joints );
|
||||
int NearestJoint( int a, int b, int c ) const;
|
||||
int NumVerts( void ) const;
|
||||
int NumTris( void ) const;
|
||||
int NumWeights( void ) const;
|
||||
|
||||
private:
|
||||
idList<idVec2> texCoords; // texture coordinates
|
||||
int numWeights; // number of weights
|
||||
idVec4 * scaledWeights; // joint weights
|
||||
int * weightIndex; // pairs of: joint offset + bool true if next weight is for next vertex
|
||||
const idMaterial * shader; // material applied to mesh
|
||||
int numTris; // number of triangles
|
||||
struct deformInfo_s * deformInfo; // used to create srfTriangles_t from base frames and new vertexes
|
||||
int surfaceNum; // number of the static surface created for this mesh
|
||||
|
||||
void TransformVerts( idDrawVert *verts, const idJointMat *joints );
|
||||
void TransformScaledVerts( idDrawVert *verts, const idJointMat *joints, float scale );
|
||||
};
|
||||
|
||||
class idRenderModelMD5 : public idRenderModelStatic {
|
||||
public:
|
||||
virtual void InitFromFile( const char *fileName );
|
||||
virtual dynamicModel_t IsDynamicModel() const;
|
||||
virtual idBounds Bounds( const struct renderEntity_s *ent ) const;
|
||||
virtual void Print() const;
|
||||
virtual void List() const;
|
||||
virtual void TouchData();
|
||||
virtual void PurgeModel();
|
||||
virtual void LoadModel();
|
||||
virtual int Memory() const;
|
||||
virtual idRenderModel * InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel );
|
||||
virtual int NumJoints( void ) const;
|
||||
virtual const idMD5Joint * GetJoints( void ) const;
|
||||
virtual jointHandle_t GetJointHandle( const char *name ) const;
|
||||
virtual const char * GetJointName( jointHandle_t handle ) const;
|
||||
virtual const idJointQuat * GetDefaultPose( void ) const;
|
||||
virtual int NearestJoint( int surfaceNum, int a, int b, int c ) const;
|
||||
|
||||
private:
|
||||
idList<idMD5Joint> joints;
|
||||
idList<idJointQuat> defaultPose;
|
||||
idList<idMD5Mesh> meshes;
|
||||
|
||||
void CalculateBounds( const idJointMat *joints );
|
||||
void GetFrameBounds( const renderEntity_t *ent, idBounds &bounds ) const;
|
||||
void DrawJoints( const renderEntity_t *ent, const struct viewDef_s *view ) const;
|
||||
void ParseJoint( idLexer &parser, idMD5Joint *joint, idJointQuat *defaultPose );
|
||||
};
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
MD3 animated model
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
struct md3Header_s;
|
||||
struct md3Surface_s;
|
||||
|
||||
class idRenderModelMD3 : public idRenderModelStatic {
|
||||
public:
|
||||
virtual void InitFromFile( const char *fileName );
|
||||
virtual dynamicModel_t IsDynamicModel() const;
|
||||
virtual idRenderModel * InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel );
|
||||
virtual idBounds Bounds( const struct renderEntity_s *ent ) const;
|
||||
|
||||
private:
|
||||
int index; // model = tr.models[model->index]
|
||||
int dataSize; // just for listing purposes
|
||||
struct md3Header_s * md3; // only if type == MOD_MESH
|
||||
int numLods;
|
||||
|
||||
void LerpMeshVertexes( srfTriangles_t *tri, const struct md3Surface_s *surf, const float backlerp, const int frame, const int oldframe ) const;
|
||||
};
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Liquid model
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idRenderModelLiquid : public idRenderModelStatic {
|
||||
public:
|
||||
idRenderModelLiquid();
|
||||
|
||||
virtual void InitFromFile( const char *fileName );
|
||||
virtual dynamicModel_t IsDynamicModel() const;
|
||||
virtual idRenderModel * InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel );
|
||||
virtual idBounds Bounds( const struct renderEntity_s *ent ) const;
|
||||
|
||||
virtual void Reset();
|
||||
void IntersectBounds( const idBounds &bounds, float displacement );
|
||||
|
||||
private:
|
||||
modelSurface_t GenerateSurface( float lerp );
|
||||
void WaterDrop( int x, int y, float *page );
|
||||
void Update( void );
|
||||
|
||||
int verts_x;
|
||||
int verts_y;
|
||||
float scale_x;
|
||||
float scale_y;
|
||||
int time;
|
||||
int liquid_type;
|
||||
int update_tics;
|
||||
int seed;
|
||||
|
||||
idRandom random;
|
||||
|
||||
const idMaterial * shader;
|
||||
struct deformInfo_s * deformInfo; // used to create srfTriangles_t from base frames
|
||||
// and new vertexes
|
||||
|
||||
float density;
|
||||
float drop_height;
|
||||
int drop_radius;
|
||||
float drop_delay;
|
||||
|
||||
idList<float> pages;
|
||||
float * page1;
|
||||
float * page2;
|
||||
|
||||
idList<idDrawVert> verts;
|
||||
|
||||
int nextDropTime;
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
PRT model
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idRenderModelPrt : public idRenderModelStatic {
|
||||
public:
|
||||
idRenderModelPrt();
|
||||
|
||||
virtual void InitFromFile( const char *fileName );
|
||||
virtual void TouchData();
|
||||
virtual dynamicModel_t IsDynamicModel() const;
|
||||
virtual idRenderModel * InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel );
|
||||
virtual idBounds Bounds( const struct renderEntity_s *ent ) const;
|
||||
virtual float DepthHack() const;
|
||||
virtual int Memory() const;
|
||||
|
||||
private:
|
||||
const idDeclParticle * particleSystem;
|
||||
};
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Beam model
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idRenderModelBeam : public idRenderModelStatic {
|
||||
public:
|
||||
virtual dynamicModel_t IsDynamicModel() const;
|
||||
virtual bool IsLoaded() const;
|
||||
virtual idRenderModel * InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel );
|
||||
virtual idBounds Bounds( const struct renderEntity_s *ent ) const;
|
||||
};
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Beam model
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
#define MAX_TRAIL_PTS 20
|
||||
|
||||
struct Trail_t {
|
||||
int lastUpdateTime;
|
||||
int duration;
|
||||
|
||||
idVec3 pts[MAX_TRAIL_PTS];
|
||||
int numPoints;
|
||||
};
|
||||
|
||||
class idRenderModelTrail : public idRenderModelStatic {
|
||||
idList<Trail_t> trails;
|
||||
int numActive;
|
||||
idBounds trailBounds;
|
||||
|
||||
public:
|
||||
idRenderModelTrail();
|
||||
|
||||
virtual dynamicModel_t IsDynamicModel() const;
|
||||
virtual bool IsLoaded() const;
|
||||
virtual idRenderModel * InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel );
|
||||
virtual idBounds Bounds( const struct renderEntity_s *ent ) const;
|
||||
|
||||
int NewTrail( idVec3 pt, int duration );
|
||||
void UpdateTrail( int index, idVec3 pt );
|
||||
void DrawTrail( int index, const struct renderEntity_s *ent, srfTriangles_t *tri, float globalAlpha );
|
||||
};
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Lightning model
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idRenderModelLightning : public idRenderModelStatic {
|
||||
public:
|
||||
virtual dynamicModel_t IsDynamicModel() const;
|
||||
virtual bool IsLoaded() const;
|
||||
virtual idRenderModel * InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel );
|
||||
virtual idBounds Bounds( const struct renderEntity_s *ent ) const;
|
||||
};
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
idRenderModelSprite
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
class idRenderModelSprite : public idRenderModelStatic {
|
||||
public:
|
||||
virtual dynamicModel_t IsDynamicModel() const;
|
||||
virtual bool IsLoaded() const;
|
||||
virtual idRenderModel * InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel );
|
||||
virtual idBounds Bounds( const struct renderEntity_s *ent ) const;
|
||||
};
|
||||
|
||||
#endif /* !__MODEL_LOCAL_H__ */
|
||||
4138
neo/renderer/Model_lwo.cpp
Normal file
4138
neo/renderer/Model_lwo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
676
neo/renderer/Model_lwo.h
Normal file
676
neo/renderer/Model_lwo.h
Normal file
@@ -0,0 +1,676 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __LWO2_H__
|
||||
#define __LWO2_H__
|
||||
|
||||
/*
|
||||
======================================================================
|
||||
|
||||
LWO2 loader. (LightWave Object)
|
||||
|
||||
Ernie Wright 17 Sep 00
|
||||
|
||||
======================================================================
|
||||
*/
|
||||
|
||||
/* chunk and subchunk IDs */
|
||||
|
||||
#define LWID_(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d))
|
||||
|
||||
#define ID_FORM LWID_('F','O','R','M')
|
||||
#define ID_LWO2 LWID_('L','W','O','2')
|
||||
#define ID_LWOB LWID_('L','W','O','B')
|
||||
|
||||
/* top-level chunks */
|
||||
#define ID_LAYR LWID_('L','A','Y','R')
|
||||
#define ID_TAGS LWID_('T','A','G','S')
|
||||
#define ID_PNTS LWID_('P','N','T','S')
|
||||
#define ID_BBOX LWID_('B','B','O','X')
|
||||
#define ID_VMAP LWID_('V','M','A','P')
|
||||
#define ID_VMAD LWID_('V','M','A','D')
|
||||
#define ID_POLS LWID_('P','O','L','S')
|
||||
#define ID_PTAG LWID_('P','T','A','G')
|
||||
#define ID_ENVL LWID_('E','N','V','L')
|
||||
#define ID_CLIP LWID_('C','L','I','P')
|
||||
#define ID_SURF LWID_('S','U','R','F')
|
||||
#define ID_DESC LWID_('D','E','S','C')
|
||||
#define ID_TEXT LWID_('T','E','X','T')
|
||||
#define ID_ICON LWID_('I','C','O','N')
|
||||
|
||||
/* polygon types */
|
||||
#define ID_FACE LWID_('F','A','C','E')
|
||||
#define ID_CURV LWID_('C','U','R','V')
|
||||
#define ID_PTCH LWID_('P','T','C','H')
|
||||
#define ID_MBAL LWID_('M','B','A','L')
|
||||
#define ID_BONE LWID_('B','O','N','E')
|
||||
|
||||
/* polygon tags */
|
||||
#define ID_SURF LWID_('S','U','R','F')
|
||||
#define ID_PART LWID_('P','A','R','T')
|
||||
#define ID_SMGP LWID_('S','M','G','P')
|
||||
|
||||
/* envelopes */
|
||||
#define ID_PRE LWID_('P','R','E',' ')
|
||||
#define ID_POST LWID_('P','O','S','T')
|
||||
#define ID_KEY LWID_('K','E','Y',' ')
|
||||
#define ID_SPAN LWID_('S','P','A','N')
|
||||
#define ID_TCB LWID_('T','C','B',' ')
|
||||
#define ID_HERM LWID_('H','E','R','M')
|
||||
#define ID_BEZI LWID_('B','E','Z','I')
|
||||
#define ID_BEZ2 LWID_('B','E','Z','2')
|
||||
#define ID_LINE LWID_('L','I','N','E')
|
||||
#define ID_STEP LWID_('S','T','E','P')
|
||||
|
||||
/* clips */
|
||||
#define ID_STIL LWID_('S','T','I','L')
|
||||
#define ID_ISEQ LWID_('I','S','E','Q')
|
||||
#define ID_ANIM LWID_('A','N','I','M')
|
||||
#define ID_XREF LWID_('X','R','E','F')
|
||||
#define ID_STCC LWID_('S','T','C','C')
|
||||
#define ID_TIME LWID_('T','I','M','E')
|
||||
#define ID_CONT LWID_('C','O','N','T')
|
||||
#define ID_BRIT LWID_('B','R','I','T')
|
||||
#define ID_SATR LWID_('S','A','T','R')
|
||||
#define ID_HUE LWID_('H','U','E',' ')
|
||||
#define ID_GAMM LWID_('G','A','M','M')
|
||||
#define ID_NEGA LWID_('N','E','G','A')
|
||||
#define ID_IFLT LWID_('I','F','L','T')
|
||||
#define ID_PFLT LWID_('P','F','L','T')
|
||||
|
||||
/* surfaces */
|
||||
#define ID_COLR LWID_('C','O','L','R')
|
||||
#define ID_LUMI LWID_('L','U','M','I')
|
||||
#define ID_DIFF LWID_('D','I','F','F')
|
||||
#define ID_SPEC LWID_('S','P','E','C')
|
||||
#define ID_GLOS LWID_('G','L','O','S')
|
||||
#define ID_REFL LWID_('R','E','F','L')
|
||||
#define ID_RFOP LWID_('R','F','O','P')
|
||||
#define ID_RIMG LWID_('R','I','M','G')
|
||||
#define ID_RSAN LWID_('R','S','A','N')
|
||||
#define ID_TRAN LWID_('T','R','A','N')
|
||||
#define ID_TROP LWID_('T','R','O','P')
|
||||
#define ID_TIMG LWID_('T','I','M','G')
|
||||
#define ID_RIND LWID_('R','I','N','D')
|
||||
#define ID_TRNL LWID_('T','R','N','L')
|
||||
#define ID_BUMP LWID_('B','U','M','P')
|
||||
#define ID_SMAN LWID_('S','M','A','N')
|
||||
#define ID_SIDE LWID_('S','I','D','E')
|
||||
#define ID_CLRH LWID_('C','L','R','H')
|
||||
#define ID_CLRF LWID_('C','L','R','F')
|
||||
#define ID_ADTR LWID_('A','D','T','R')
|
||||
#define ID_SHRP LWID_('S','H','R','P')
|
||||
#define ID_LINE LWID_('L','I','N','E')
|
||||
#define ID_LSIZ LWID_('L','S','I','Z')
|
||||
#define ID_ALPH LWID_('A','L','P','H')
|
||||
#define ID_AVAL LWID_('A','V','A','L')
|
||||
#define ID_GVAL LWID_('G','V','A','L')
|
||||
#define ID_BLOK LWID_('B','L','O','K')
|
||||
|
||||
/* texture layer */
|
||||
#define ID_TYPE LWID_('T','Y','P','E')
|
||||
#define ID_CHAN LWID_('C','H','A','N')
|
||||
#define ID_NAME LWID_('N','A','M','E')
|
||||
#define ID_ENAB LWID_('E','N','A','B')
|
||||
#define ID_OPAC LWID_('O','P','A','C')
|
||||
#define ID_FLAG LWID_('F','L','A','G')
|
||||
#define ID_PROJ LWID_('P','R','O','J')
|
||||
#define ID_STCK LWID_('S','T','C','K')
|
||||
#define ID_TAMP LWID_('T','A','M','P')
|
||||
|
||||
/* texture coordinates */
|
||||
#define ID_TMAP LWID_('T','M','A','P')
|
||||
#define ID_AXIS LWID_('A','X','I','S')
|
||||
#define ID_CNTR LWID_('C','N','T','R')
|
||||
#define ID_SIZE LWID_('S','I','Z','E')
|
||||
#define ID_ROTA LWID_('R','O','T','A')
|
||||
#define ID_OREF LWID_('O','R','E','F')
|
||||
#define ID_FALL LWID_('F','A','L','L')
|
||||
#define ID_CSYS LWID_('C','S','Y','S')
|
||||
|
||||
/* image map */
|
||||
#define ID_IMAP LWID_('I','M','A','P')
|
||||
#define ID_IMAG LWID_('I','M','A','G')
|
||||
#define ID_WRAP LWID_('W','R','A','P')
|
||||
#define ID_WRPW LWID_('W','R','P','W')
|
||||
#define ID_WRPH LWID_('W','R','P','H')
|
||||
#define ID_VMAP LWID_('V','M','A','P')
|
||||
#define ID_AAST LWID_('A','A','S','T')
|
||||
#define ID_PIXB LWID_('P','I','X','B')
|
||||
|
||||
/* procedural */
|
||||
#define ID_PROC LWID_('P','R','O','C')
|
||||
#define ID_COLR LWID_('C','O','L','R')
|
||||
#define ID_VALU LWID_('V','A','L','U')
|
||||
#define ID_FUNC LWID_('F','U','N','C')
|
||||
#define ID_FTPS LWID_('F','T','P','S')
|
||||
#define ID_ITPS LWID_('I','T','P','S')
|
||||
#define ID_ETPS LWID_('E','T','P','S')
|
||||
|
||||
/* gradient */
|
||||
#define ID_GRAD LWID_('G','R','A','D')
|
||||
#define ID_GRST LWID_('G','R','S','T')
|
||||
#define ID_GREN LWID_('G','R','E','N')
|
||||
#define ID_PNAM LWID_('P','N','A','M')
|
||||
#define ID_INAM LWID_('I','N','A','M')
|
||||
#define ID_GRPT LWID_('G','R','P','T')
|
||||
#define ID_FKEY LWID_('F','K','E','Y')
|
||||
#define ID_IKEY LWID_('I','K','E','Y')
|
||||
|
||||
/* shader */
|
||||
#define ID_SHDR LWID_('S','H','D','R')
|
||||
#define ID_DATA LWID_('D','A','T','A')
|
||||
|
||||
|
||||
/* generic linked list */
|
||||
|
||||
typedef struct st_lwNode {
|
||||
struct st_lwNode *next, *prev;
|
||||
void *data;
|
||||
} lwNode;
|
||||
|
||||
|
||||
/* plug-in reference */
|
||||
|
||||
typedef struct st_lwPlugin {
|
||||
struct st_lwPlugin *next, *prev;
|
||||
char *ord;
|
||||
char *name;
|
||||
int flags;
|
||||
void *data;
|
||||
} lwPlugin;
|
||||
|
||||
|
||||
/* envelopes */
|
||||
|
||||
typedef struct st_lwKey {
|
||||
struct st_lwKey *next, *prev;
|
||||
float value;
|
||||
float time;
|
||||
unsigned int shape; /* ID_TCB, ID_BEZ2, etc. */
|
||||
float tension;
|
||||
float continuity;
|
||||
float bias;
|
||||
float param[ 4 ];
|
||||
} lwKey;
|
||||
|
||||
typedef struct st_lwEnvelope {
|
||||
struct st_lwEnvelope *next, *prev;
|
||||
int index;
|
||||
int type;
|
||||
char *name;
|
||||
lwKey *key; /* linked list of keys */
|
||||
int nkeys;
|
||||
int behavior[ 2 ]; /* pre and post (extrapolation) */
|
||||
lwPlugin *cfilter; /* linked list of channel filters */
|
||||
int ncfilters;
|
||||
} lwEnvelope;
|
||||
|
||||
#define BEH_RESET 0
|
||||
#define BEH_CONSTANT 1
|
||||
#define BEH_REPEAT 2
|
||||
#define BEH_OSCILLATE 3
|
||||
#define BEH_OFFSET 4
|
||||
#define BEH_LINEAR 5
|
||||
|
||||
|
||||
/* values that can be enveloped */
|
||||
|
||||
typedef struct st_lwEParam {
|
||||
float val;
|
||||
int eindex;
|
||||
} lwEParam;
|
||||
|
||||
typedef struct st_lwVParam {
|
||||
float val[ 3 ];
|
||||
int eindex;
|
||||
} lwVParam;
|
||||
|
||||
|
||||
/* clips */
|
||||
|
||||
typedef struct st_lwClipStill {
|
||||
char *name;
|
||||
} lwClipStill;
|
||||
|
||||
typedef struct st_lwClipSeq {
|
||||
char *prefix; /* filename before sequence digits */
|
||||
char *suffix; /* after digits, e.g. extensions */
|
||||
int digits;
|
||||
int flags;
|
||||
int offset;
|
||||
int start;
|
||||
int end;
|
||||
} lwClipSeq;
|
||||
|
||||
typedef struct st_lwClipAnim {
|
||||
char *name;
|
||||
char *server; /* anim loader plug-in */
|
||||
void *data;
|
||||
} lwClipAnim;
|
||||
|
||||
typedef struct st_lwClipXRef {
|
||||
char *string;
|
||||
int index;
|
||||
struct st_lwClip *clip;
|
||||
} lwClipXRef;
|
||||
|
||||
typedef struct st_lwClipCycle {
|
||||
char *name;
|
||||
int lo;
|
||||
int hi;
|
||||
} lwClipCycle;
|
||||
|
||||
typedef struct st_lwClip {
|
||||
struct st_lwClip *next, *prev;
|
||||
int index;
|
||||
unsigned int type; /* ID_STIL, ID_ISEQ, etc. */
|
||||
union {
|
||||
lwClipStill still;
|
||||
lwClipSeq seq;
|
||||
lwClipAnim anim;
|
||||
lwClipXRef xref;
|
||||
lwClipCycle cycle;
|
||||
} source;
|
||||
float start_time;
|
||||
float duration;
|
||||
float frame_rate;
|
||||
lwEParam contrast;
|
||||
lwEParam brightness;
|
||||
lwEParam saturation;
|
||||
lwEParam hue;
|
||||
lwEParam gamma;
|
||||
int negative;
|
||||
lwPlugin *ifilter; /* linked list of image filters */
|
||||
int nifilters;
|
||||
lwPlugin *pfilter; /* linked list of pixel filters */
|
||||
int npfilters;
|
||||
} lwClip;
|
||||
|
||||
|
||||
/* textures */
|
||||
|
||||
typedef struct st_lwTMap {
|
||||
lwVParam size;
|
||||
lwVParam center;
|
||||
lwVParam rotate;
|
||||
lwVParam falloff;
|
||||
int fall_type;
|
||||
char *ref_object;
|
||||
int coord_sys;
|
||||
} lwTMap;
|
||||
|
||||
typedef struct st_lwImageMap {
|
||||
int cindex;
|
||||
int projection;
|
||||
char *vmap_name;
|
||||
int axis;
|
||||
int wrapw_type;
|
||||
int wraph_type;
|
||||
lwEParam wrapw;
|
||||
lwEParam wraph;
|
||||
float aa_strength;
|
||||
int aas_flags;
|
||||
int pblend;
|
||||
lwEParam stck;
|
||||
lwEParam amplitude;
|
||||
} lwImageMap;
|
||||
|
||||
#define PROJ_PLANAR 0
|
||||
#define PROJ_CYLINDRICAL 1
|
||||
#define PROJ_SPHERICAL 2
|
||||
#define PROJ_CUBIC 3
|
||||
#define PROJ_FRONT 4
|
||||
|
||||
#define WRAP_NONE 0
|
||||
#define WRAP_EDGE 1
|
||||
#define WRAP_REPEAT 2
|
||||
#define WRAP_MIRROR 3
|
||||
|
||||
typedef struct st_lwProcedural {
|
||||
int axis;
|
||||
float value[ 3 ];
|
||||
char *name;
|
||||
void *data;
|
||||
} lwProcedural;
|
||||
|
||||
typedef struct st_lwGradKey {
|
||||
struct st_lwGradKey *next, *prev;
|
||||
float value;
|
||||
float rgba[ 4 ];
|
||||
} lwGradKey;
|
||||
|
||||
typedef struct st_lwGradient {
|
||||
char *paramname;
|
||||
char *itemname;
|
||||
float start;
|
||||
float end;
|
||||
int repeat;
|
||||
lwGradKey *key; /* array of gradient keys */
|
||||
short *ikey; /* array of interpolation codes */
|
||||
} lwGradient;
|
||||
|
||||
typedef struct st_lwTexture {
|
||||
struct st_lwTexture *next, *prev;
|
||||
char *ord;
|
||||
unsigned int type;
|
||||
unsigned int chan;
|
||||
lwEParam opacity;
|
||||
short opac_type;
|
||||
short enabled;
|
||||
short negative;
|
||||
short axis;
|
||||
union {
|
||||
lwImageMap imap;
|
||||
lwProcedural proc;
|
||||
lwGradient grad;
|
||||
} param;
|
||||
lwTMap tmap;
|
||||
} lwTexture;
|
||||
|
||||
|
||||
/* values that can be textured */
|
||||
|
||||
typedef struct st_lwTParam {
|
||||
float val;
|
||||
int eindex;
|
||||
lwTexture *tex; /* linked list of texture layers */
|
||||
} lwTParam;
|
||||
|
||||
typedef struct st_lwCParam {
|
||||
float rgb[ 3 ];
|
||||
int eindex;
|
||||
lwTexture *tex; /* linked list of texture layers */
|
||||
} lwCParam;
|
||||
|
||||
|
||||
/* surfaces */
|
||||
|
||||
typedef struct st_lwGlow {
|
||||
short enabled;
|
||||
short type;
|
||||
lwEParam intensity;
|
||||
lwEParam size;
|
||||
} Glow;
|
||||
|
||||
typedef struct st_lwRMap {
|
||||
lwTParam val;
|
||||
int options;
|
||||
int cindex;
|
||||
float seam_angle;
|
||||
} lwRMap;
|
||||
|
||||
typedef struct st_lwLine {
|
||||
short enabled;
|
||||
unsigned short flags;
|
||||
lwEParam size;
|
||||
} lwLine;
|
||||
|
||||
typedef struct st_lwSurface {
|
||||
struct st_lwSurface *next, *prev;
|
||||
char *name;
|
||||
char *srcname;
|
||||
lwCParam color;
|
||||
lwTParam luminosity;
|
||||
lwTParam diffuse;
|
||||
lwTParam specularity;
|
||||
lwTParam glossiness;
|
||||
lwRMap reflection;
|
||||
lwRMap transparency;
|
||||
lwTParam eta;
|
||||
lwTParam translucency;
|
||||
lwTParam bump;
|
||||
float smooth;
|
||||
int sideflags;
|
||||
float alpha;
|
||||
int alpha_mode;
|
||||
lwEParam color_hilite;
|
||||
lwEParam color_filter;
|
||||
lwEParam add_trans;
|
||||
lwEParam dif_sharp;
|
||||
lwEParam glow;
|
||||
lwLine line;
|
||||
lwPlugin *shader; /* linked list of shaders */
|
||||
int nshaders;
|
||||
} lwSurface;
|
||||
|
||||
|
||||
/* vertex maps */
|
||||
|
||||
typedef struct st_lwVMap {
|
||||
struct st_lwVMap *next, *prev;
|
||||
char *name;
|
||||
unsigned int type;
|
||||
int dim;
|
||||
int nverts;
|
||||
int perpoly;
|
||||
int *vindex; /* array of point indexes */
|
||||
int *pindex; /* array of polygon indexes */
|
||||
float **val;
|
||||
|
||||
// added by duffy
|
||||
int offset;
|
||||
} lwVMap;
|
||||
|
||||
typedef struct st_lwVMapPt {
|
||||
lwVMap *vmap;
|
||||
int index; /* vindex or pindex element */
|
||||
} lwVMapPt;
|
||||
|
||||
|
||||
/* points and polygons */
|
||||
|
||||
typedef struct st_lwPoint {
|
||||
float pos[ 3 ];
|
||||
int npols; /* number of polygons sharing the point */
|
||||
int *pol; /* array of polygon indexes */
|
||||
int nvmaps;
|
||||
lwVMapPt *vm; /* array of vmap references */
|
||||
} lwPoint;
|
||||
|
||||
typedef struct st_lwPolVert {
|
||||
int index; /* index into the point array */
|
||||
float norm[ 3 ];
|
||||
int nvmaps;
|
||||
lwVMapPt *vm; /* array of vmap references */
|
||||
} lwPolVert;
|
||||
|
||||
typedef struct st_lwPolygon {
|
||||
lwSurface *surf;
|
||||
int part; /* part index */
|
||||
int smoothgrp; /* smoothing group */
|
||||
int flags;
|
||||
unsigned int type;
|
||||
float norm[ 3 ];
|
||||
int nverts;
|
||||
lwPolVert *v; /* array of vertex records */
|
||||
} lwPolygon;
|
||||
|
||||
typedef struct st_lwPointList {
|
||||
int count;
|
||||
int offset; /* only used during reading */
|
||||
lwPoint *pt; /* array of points */
|
||||
} lwPointList;
|
||||
|
||||
typedef struct st_lwPolygonList {
|
||||
int count;
|
||||
int offset; /* only used during reading */
|
||||
int vcount; /* total number of vertices */
|
||||
int voffset; /* only used during reading */
|
||||
lwPolygon *pol; /* array of polygons */
|
||||
} lwPolygonList;
|
||||
|
||||
|
||||
/* geometry layers */
|
||||
|
||||
typedef struct st_lwLayer {
|
||||
struct st_lwLayer *next, *prev;
|
||||
char *name;
|
||||
int index;
|
||||
int parent;
|
||||
int flags;
|
||||
float pivot[ 3 ];
|
||||
float bbox[ 6 ];
|
||||
lwPointList point;
|
||||
lwPolygonList polygon;
|
||||
int nvmaps;
|
||||
lwVMap *vmap; /* linked list of vmaps */
|
||||
} lwLayer;
|
||||
|
||||
|
||||
/* tag strings */
|
||||
|
||||
typedef struct st_lwTagList {
|
||||
int count;
|
||||
int offset; /* only used during reading */
|
||||
char **tag; /* array of strings */
|
||||
} lwTagList;
|
||||
|
||||
|
||||
/* an object */
|
||||
|
||||
typedef struct st_lwObject {
|
||||
ID_TIME_T timeStamp;
|
||||
lwLayer * layer; /* linked list of layers */
|
||||
lwEnvelope * env; /* linked list of envelopes */
|
||||
lwClip * clip; /* linked list of clips */
|
||||
lwSurface * surf; /* linked list of surfaces */
|
||||
lwTagList taglist;
|
||||
int nlayers;
|
||||
int nenvs;
|
||||
int nclips;
|
||||
int nsurfs;
|
||||
} lwObject;
|
||||
|
||||
|
||||
/* lwo2.c */
|
||||
|
||||
lwObject *lwGetObject( const char *filename, unsigned int *failID, int *failpos );
|
||||
void lwFreeObject( lwObject *object );
|
||||
void lwFreeLayer( lwLayer *layer );
|
||||
|
||||
/* pntspols.c */
|
||||
|
||||
void lwFreePoints( lwPointList *point );
|
||||
void lwFreePolygons( lwPolygonList *plist );
|
||||
int lwGetPoints( idFile *fp, int cksize, lwPointList *point );
|
||||
void lwGetBoundingBox( lwPointList *point, float bbox[] );
|
||||
int lwAllocPolygons( lwPolygonList *plist, int npols, int nverts );
|
||||
int lwGetPolygons( idFile *fp, int cksize, lwPolygonList *plist, int ptoffset );
|
||||
void lwGetPolyNormals( lwPointList *point, lwPolygonList *polygon );
|
||||
int lwGetPointPolygons( lwPointList *point, lwPolygonList *polygon );
|
||||
int lwResolvePolySurfaces( lwPolygonList *polygon, lwTagList *tlist,
|
||||
lwSurface **surf, int *nsurfs );
|
||||
void lwGetVertNormals( lwPointList *point, lwPolygonList *polygon );
|
||||
void lwFreeTags( lwTagList *tlist );
|
||||
int lwGetTags( idFile *fp, int cksize, lwTagList *tlist );
|
||||
int lwGetPolygonTags( idFile *fp, int cksize, lwTagList *tlist,
|
||||
lwPolygonList *plist );
|
||||
|
||||
/* vmap.c */
|
||||
|
||||
void lwFreeVMap( lwVMap *vmap );
|
||||
lwVMap *lwGetVMap( idFile *fp, int cksize, int ptoffset, int poloffset,
|
||||
int perpoly );
|
||||
int lwGetPointVMaps( lwPointList *point, lwVMap *vmap );
|
||||
int lwGetPolyVMaps( lwPolygonList *polygon, lwVMap *vmap );
|
||||
|
||||
/* clip.c */
|
||||
|
||||
void lwFreeClip( lwClip *clip );
|
||||
lwClip *lwGetClip( idFile *fp, int cksize );
|
||||
lwClip *lwFindClip( lwClip *list, int index );
|
||||
|
||||
/* envelope.c */
|
||||
|
||||
void lwFreeEnvelope( lwEnvelope *env );
|
||||
lwEnvelope *lwGetEnvelope( idFile *fp, int cksize );
|
||||
lwEnvelope *lwFindEnvelope( lwEnvelope *list, int index );
|
||||
float lwEvalEnvelope( lwEnvelope *env, float time );
|
||||
|
||||
/* surface.c */
|
||||
|
||||
void lwFreePlugin( lwPlugin *p );
|
||||
void lwFreeTexture( lwTexture *t );
|
||||
void lwFreeSurface( lwSurface *surf );
|
||||
int lwGetTHeader( idFile *fp, int hsz, lwTexture *tex );
|
||||
int lwGetTMap( idFile *fp, int tmapsz, lwTMap *tmap );
|
||||
int lwGetImageMap( idFile *fp, int rsz, lwTexture *tex );
|
||||
int lwGetProcedural( idFile *fp, int rsz, lwTexture *tex );
|
||||
int lwGetGradient( idFile *fp, int rsz, lwTexture *tex );
|
||||
lwTexture *lwGetTexture( idFile *fp, int bloksz, unsigned int type );
|
||||
lwPlugin *lwGetShader( idFile *fp, int bloksz );
|
||||
lwSurface *lwGetSurface( idFile *fp, int cksize );
|
||||
lwSurface *lwDefaultSurface( void );
|
||||
|
||||
/* lwob.c */
|
||||
|
||||
lwSurface *lwGetSurface5( idFile *fp, int cksize, lwObject *obj );
|
||||
int lwGetPolygons5( idFile *fp, int cksize, lwPolygonList *plist, int ptoffset );
|
||||
lwObject *lwGetObject5( const char *filename, unsigned int *failID, int *failpos );
|
||||
|
||||
/* list.c */
|
||||
|
||||
void lwListFree( void *list, void ( *freeNode )( void * ));
|
||||
void lwListAdd( void **list, void *node );
|
||||
void lwListInsert( void **vlist, void *vitem,
|
||||
int ( *compare )( void *, void * ));
|
||||
|
||||
/* vecmath.c */
|
||||
|
||||
float dot( float a[], float b[] );
|
||||
void cross( float a[], float b[], float c[] );
|
||||
void normalize( float v[] );
|
||||
#define vecangle( a, b ) ( float ) idMath::ACos( dot( a, b ) )
|
||||
|
||||
/* lwio.c */
|
||||
|
||||
void set_flen( int i );
|
||||
int get_flen( void );
|
||||
void *getbytes( idFile *fp, int size );
|
||||
void skipbytes( idFile *fp, int n );
|
||||
int getI1( idFile *fp );
|
||||
short getI2( idFile *fp );
|
||||
int getI4( idFile *fp );
|
||||
unsigned char getU1( idFile *fp );
|
||||
unsigned short getU2( idFile *fp );
|
||||
unsigned int getU4( idFile *fp );
|
||||
int getVX( idFile *fp );
|
||||
float getF4( idFile *fp );
|
||||
char *getS0( idFile *fp );
|
||||
int sgetI1( unsigned char **bp );
|
||||
short sgetI2( unsigned char **bp );
|
||||
int sgetI4( unsigned char **bp );
|
||||
unsigned char sgetU1( unsigned char **bp );
|
||||
unsigned short sgetU2( unsigned char **bp );
|
||||
unsigned int sgetU4( unsigned char **bp );
|
||||
int sgetVX( unsigned char **bp );
|
||||
float sgetF4( unsigned char **bp );
|
||||
char *sgetS0( unsigned char **bp );
|
||||
|
||||
#endif /* !__LWO2_H__ */
|
||||
1107
neo/renderer/Model_ma.cpp
Normal file
1107
neo/renderer/Model_ma.cpp
Normal file
File diff suppressed because it is too large
Load Diff
145
neo/renderer/Model_ma.h
Normal file
145
neo/renderer/Model_ma.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __MODEL_MA_H__
|
||||
#define __MODEL_MA_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
MA loader. (Maya Ascii Format)
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
char name[128];
|
||||
char parent[128];
|
||||
} maNodeHeader_t;
|
||||
|
||||
typedef struct {
|
||||
char name[128];
|
||||
int size;
|
||||
} maAttribHeader_t;
|
||||
|
||||
typedef struct maTransform_s {
|
||||
idVec3 translate;
|
||||
idVec3 rotate;
|
||||
idVec3 scale;
|
||||
maTransform_s* parent;
|
||||
} maTransform_t;
|
||||
|
||||
typedef struct {
|
||||
int edge[3];
|
||||
int vertexNum[3];
|
||||
int tVertexNum[3];
|
||||
int vertexColors[3];
|
||||
idVec3 vertexNormals[3];
|
||||
} maFace_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
//Transform to be applied
|
||||
maTransform_t* transform;
|
||||
|
||||
//Verts
|
||||
int numVertexes;
|
||||
idVec3 * vertexes;
|
||||
int numVertTransforms;
|
||||
idVec4 * vertTransforms;
|
||||
int nextVertTransformIndex;
|
||||
|
||||
//Texture Coordinates
|
||||
int numTVertexes;
|
||||
idVec2 * tvertexes;
|
||||
|
||||
//Edges
|
||||
int numEdges;
|
||||
idVec3 * edges;
|
||||
|
||||
//Colors
|
||||
int numColors;
|
||||
byte* colors;
|
||||
|
||||
//Faces
|
||||
int numFaces;
|
||||
maFace_t * faces;
|
||||
|
||||
//Normals
|
||||
int numNormals;
|
||||
idVec3 * normals;
|
||||
bool normalsParsed;
|
||||
int nextNormal;
|
||||
|
||||
} maMesh_t;
|
||||
|
||||
typedef struct {
|
||||
char name[128];
|
||||
float uOffset, vOffset; // max lets you offset by material without changing texCoords
|
||||
float uTiling, vTiling; // multiply tex coords by this
|
||||
float angle; // in clockwise radians
|
||||
} maMaterial_t;
|
||||
|
||||
typedef struct {
|
||||
char name[128];
|
||||
int materialRef;
|
||||
char materialName[128];
|
||||
|
||||
maMesh_t mesh;
|
||||
} maObject_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
char name[128];
|
||||
char path[1024];
|
||||
} maFileNode_t;
|
||||
|
||||
typedef struct maMaterialNode_s {
|
||||
char name[128];
|
||||
|
||||
maMaterialNode_s* child;
|
||||
maFileNode_t* file;
|
||||
|
||||
} maMaterialNode_t;
|
||||
|
||||
typedef struct maModel_s {
|
||||
ID_TIME_T timeStamp;
|
||||
idList<maMaterial_t *> materials;
|
||||
idList<maObject_t *> objects;
|
||||
idHashTable<maTransform_t*> transforms;
|
||||
|
||||
//Material Resolution
|
||||
idHashTable<maFileNode_t*> fileNodes;
|
||||
idHashTable<maMaterialNode_t*> materialNodes;
|
||||
|
||||
} maModel_t;
|
||||
|
||||
maModel_t *MA_Load( const char *fileName );
|
||||
void MA_Free( maModel_t *ma );
|
||||
|
||||
#endif /* !__MODEL_MA_H__ */
|
||||
371
neo/renderer/Model_md3.cpp
Normal file
371
neo/renderer/Model_md3.cpp
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
#include "Model_local.h"
|
||||
#include "Model_md3.h"
|
||||
|
||||
/***********************************************************************
|
||||
|
||||
idMD3Mesh
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
#define LL(x) x=LittleLong(x)
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelMD3::InitFromFile
|
||||
=================
|
||||
*/
|
||||
void idRenderModelMD3::InitFromFile( const char *fileName ) {
|
||||
int i, j;
|
||||
md3Header_t *pinmodel;
|
||||
md3Frame_t *frame;
|
||||
md3Surface_t *surf;
|
||||
md3Shader_t *shader;
|
||||
md3Triangle_t *tri;
|
||||
md3St_t *st;
|
||||
md3XyzNormal_t *xyz;
|
||||
md3Tag_t *tag;
|
||||
void *buffer;
|
||||
int version;
|
||||
int size;
|
||||
|
||||
|
||||
name = fileName;
|
||||
|
||||
size = fileSystem->ReadFile( fileName, &buffer, NULL );
|
||||
if (!size || size<0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
pinmodel = (md3Header_t *)buffer;
|
||||
|
||||
version = LittleLong (pinmodel->version);
|
||||
if (version != MD3_VERSION) {
|
||||
fileSystem->FreeFile( buffer );
|
||||
common->Warning( "InitFromFile: %s has wrong version (%i should be %i)",
|
||||
fileName, version, MD3_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
size = LittleLong(pinmodel->ofsEnd);
|
||||
dataSize += size;
|
||||
md3 = (md3Header_t *)Mem_Alloc( size );
|
||||
|
||||
memcpy (md3, buffer, LittleLong(pinmodel->ofsEnd) );
|
||||
|
||||
LL(md3->ident);
|
||||
LL(md3->version);
|
||||
LL(md3->numFrames);
|
||||
LL(md3->numTags);
|
||||
LL(md3->numSurfaces);
|
||||
LL(md3->ofsFrames);
|
||||
LL(md3->ofsTags);
|
||||
LL(md3->ofsSurfaces);
|
||||
LL(md3->ofsEnd);
|
||||
|
||||
if ( md3->numFrames < 1 ) {
|
||||
common->Warning( "InitFromFile: %s has no frames", fileName );
|
||||
fileSystem->FreeFile( buffer );
|
||||
return;
|
||||
}
|
||||
|
||||
// swap all the frames
|
||||
frame = (md3Frame_t *) ( (byte *)md3 + md3->ofsFrames );
|
||||
for ( i = 0 ; i < md3->numFrames ; i++, frame++) {
|
||||
frame->radius = LittleFloat( frame->radius );
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] );
|
||||
frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] );
|
||||
frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] );
|
||||
}
|
||||
}
|
||||
|
||||
// swap all the tags
|
||||
tag = (md3Tag_t *) ( (byte *)md3 + md3->ofsTags );
|
||||
for ( i = 0 ; i < md3->numTags * md3->numFrames ; i++, tag++) {
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
tag->origin[j] = LittleFloat( tag->origin[j] );
|
||||
tag->axis[0][j] = LittleFloat( tag->axis[0][j] );
|
||||
tag->axis[1][j] = LittleFloat( tag->axis[1][j] );
|
||||
tag->axis[2][j] = LittleFloat( tag->axis[2][j] );
|
||||
}
|
||||
}
|
||||
|
||||
// swap all the surfaces
|
||||
surf = (md3Surface_t *) ( (byte *)md3 + md3->ofsSurfaces );
|
||||
for ( i = 0 ; i < md3->numSurfaces ; i++) {
|
||||
|
||||
LL(surf->ident);
|
||||
LL(surf->flags);
|
||||
LL(surf->numFrames);
|
||||
LL(surf->numShaders);
|
||||
LL(surf->numTriangles);
|
||||
LL(surf->ofsTriangles);
|
||||
LL(surf->numVerts);
|
||||
LL(surf->ofsShaders);
|
||||
LL(surf->ofsSt);
|
||||
LL(surf->ofsXyzNormals);
|
||||
LL(surf->ofsEnd);
|
||||
|
||||
if ( surf->numVerts > SHADER_MAX_VERTEXES ) {
|
||||
common->Error( "InitFromFile: %s has more than %i verts on a surface (%i)",
|
||||
fileName, SHADER_MAX_VERTEXES, surf->numVerts );
|
||||
}
|
||||
if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) {
|
||||
common->Error( "InitFromFile: %s has more than %i triangles on a surface (%i)",
|
||||
fileName, SHADER_MAX_INDEXES / 3, surf->numTriangles );
|
||||
}
|
||||
|
||||
// change to surface identifier
|
||||
surf->ident = 0; //SF_MD3;
|
||||
|
||||
// lowercase the surface name so skin compares are faster
|
||||
int slen = (int)strlen( surf->name );
|
||||
for( j = 0; j < slen; j++ ) {
|
||||
surf->name[j] = tolower( surf->name[j] );
|
||||
}
|
||||
|
||||
// strip off a trailing _1 or _2
|
||||
// this is a crutch for q3data being a mess
|
||||
j = strlen( surf->name );
|
||||
if ( j > 2 && surf->name[j-2] == '_' ) {
|
||||
surf->name[j-2] = 0;
|
||||
}
|
||||
|
||||
// register the shaders
|
||||
shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders );
|
||||
for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) {
|
||||
const idMaterial *sh;
|
||||
|
||||
sh = declManager->FindMaterial( shader->name );
|
||||
shader->shader = sh;
|
||||
}
|
||||
|
||||
// swap all the triangles
|
||||
tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles );
|
||||
for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
|
||||
LL(tri->indexes[0]);
|
||||
LL(tri->indexes[1]);
|
||||
LL(tri->indexes[2]);
|
||||
}
|
||||
|
||||
// swap all the ST
|
||||
st = (md3St_t *) ( (byte *)surf + surf->ofsSt );
|
||||
for ( j = 0 ; j < surf->numVerts ; j++, st++ ) {
|
||||
st->st[0] = LittleFloat( st->st[0] );
|
||||
st->st[1] = LittleFloat( st->st[1] );
|
||||
}
|
||||
|
||||
// swap all the XyzNormals
|
||||
xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals );
|
||||
for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ )
|
||||
{
|
||||
xyz->xyz[0] = LittleShort( xyz->xyz[0] );
|
||||
xyz->xyz[1] = LittleShort( xyz->xyz[1] );
|
||||
xyz->xyz[2] = LittleShort( xyz->xyz[2] );
|
||||
|
||||
xyz->normal = LittleShort( xyz->normal );
|
||||
}
|
||||
|
||||
|
||||
// find the next surface
|
||||
surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd );
|
||||
}
|
||||
|
||||
fileSystem->FreeFile( buffer );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelMD3::IsDynamicModel
|
||||
=================
|
||||
*/
|
||||
dynamicModel_t idRenderModelMD3::IsDynamicModel() const {
|
||||
return DM_CACHED;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelMD3::LerpMeshVertexes
|
||||
=================
|
||||
*/
|
||||
void idRenderModelMD3::LerpMeshVertexes ( srfTriangles_t *tri, const struct md3Surface_s *surf, const float backlerp, const int frame, const int oldframe ) const {
|
||||
short *oldXyz, *newXyz;
|
||||
float oldXyzScale, newXyzScale;
|
||||
int vertNum;
|
||||
int numVerts;
|
||||
|
||||
newXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + (frame * surf->numVerts * 4);
|
||||
|
||||
newXyzScale = MD3_XYZ_SCALE * (1.0 - backlerp);
|
||||
|
||||
numVerts = surf->numVerts;
|
||||
|
||||
if ( backlerp == 0 ) {
|
||||
//
|
||||
// just copy the vertexes
|
||||
//
|
||||
for (vertNum=0 ; vertNum < numVerts ; vertNum++, newXyz += 4 ) {
|
||||
|
||||
idDrawVert *outvert = &tri->verts[tri->numVerts];
|
||||
|
||||
outvert->xyz.x = newXyz[0] * newXyzScale;
|
||||
outvert->xyz.y = newXyz[1] * newXyzScale;
|
||||
outvert->xyz.z = newXyz[2] * newXyzScale;
|
||||
|
||||
tri->numVerts++;
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// interpolate and copy the vertexes
|
||||
//
|
||||
oldXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + (oldframe * surf->numVerts * 4);
|
||||
|
||||
oldXyzScale = MD3_XYZ_SCALE * backlerp;
|
||||
|
||||
for (vertNum=0 ; vertNum < numVerts ; vertNum++, oldXyz += 4, newXyz += 4 ) {
|
||||
|
||||
idDrawVert *outvert = &tri->verts[tri->numVerts];
|
||||
|
||||
// interpolate the xyz
|
||||
outvert->xyz.x = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale;
|
||||
outvert->xyz.y = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale;
|
||||
outvert->xyz.z = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale;
|
||||
|
||||
tri->numVerts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
idRenderModelMD3::InstantiateDynamicModel
|
||||
=============
|
||||
*/
|
||||
idRenderModel *idRenderModelMD3::InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) {
|
||||
int i, j;
|
||||
float backlerp;
|
||||
int * triangles;
|
||||
float * texCoords;
|
||||
int indexes;
|
||||
int numVerts;
|
||||
md3Surface_t * surface;
|
||||
int frame, oldframe;
|
||||
idRenderModelStatic *staticModel;
|
||||
|
||||
if ( cachedModel ) {
|
||||
delete cachedModel;
|
||||
cachedModel = NULL;
|
||||
}
|
||||
|
||||
staticModel = new idRenderModelStatic;
|
||||
staticModel->bounds.Clear();
|
||||
|
||||
surface = (md3Surface_t *) ((byte *)md3 + md3->ofsSurfaces);
|
||||
|
||||
// TODO: these need set by an entity
|
||||
frame = ent->shaderParms[SHADERPARM_MD3_FRAME]; // probably want to keep frames < 1000 or so
|
||||
oldframe = ent->shaderParms[SHADERPARM_MD3_LASTFRAME];
|
||||
backlerp = ent->shaderParms[SHADERPARM_MD3_BACKLERP];
|
||||
|
||||
for( i = 0; i < md3->numSurfaces; i++ ) {
|
||||
|
||||
srfTriangles_t *tri = R_AllocStaticTriSurf();
|
||||
R_AllocStaticTriSurfVerts( tri, surface->numVerts );
|
||||
R_AllocStaticTriSurfIndexes( tri, surface->numTriangles * 3 );
|
||||
tri->bounds.Clear();
|
||||
|
||||
modelSurface_t surf;
|
||||
|
||||
surf.geometry = tri;
|
||||
|
||||
md3Shader_t* shaders = (md3Shader_t *) ((byte *)surface + surface->ofsShaders);
|
||||
surf.shader = shaders->shader;
|
||||
|
||||
LerpMeshVertexes( tri, surface, backlerp, frame, oldframe );
|
||||
|
||||
triangles = (int *) ((byte *)surface + surface->ofsTriangles);
|
||||
indexes = surface->numTriangles * 3;
|
||||
for (j = 0 ; j < indexes ; j++) {
|
||||
tri->indexes[j] = triangles[j];
|
||||
}
|
||||
tri->numIndexes += indexes;
|
||||
|
||||
texCoords = (float *) ((byte *)surface + surface->ofsSt);
|
||||
|
||||
numVerts = surface->numVerts;
|
||||
for ( j = 0; j < numVerts; j++ ) {
|
||||
idDrawVert *stri = &tri->verts[j];
|
||||
stri->st[0] = texCoords[j*2+0];
|
||||
stri->st[1] = texCoords[j*2+1];
|
||||
}
|
||||
|
||||
R_BoundTriSurf( tri );
|
||||
|
||||
staticModel->AddSurface( surf );
|
||||
staticModel->bounds.AddPoint( surf.geometry->bounds[0] );
|
||||
staticModel->bounds.AddPoint( surf.geometry->bounds[1] );
|
||||
|
||||
// find the next surface
|
||||
surface = (md3Surface_t *)( (byte *)surface + surface->ofsEnd );
|
||||
}
|
||||
|
||||
return staticModel;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
idRenderModelMD3::Bounds
|
||||
=====================
|
||||
*/
|
||||
|
||||
idBounds idRenderModelMD3::Bounds(const struct renderEntity_s *ent) const {
|
||||
idBounds ret;
|
||||
|
||||
ret.Clear();
|
||||
|
||||
if (!ent || !md3) {
|
||||
// just give it the editor bounds
|
||||
ret.AddPoint(idVec3(-10,-10,-10));
|
||||
ret.AddPoint(idVec3( 10, 10, 10));
|
||||
return ret;
|
||||
}
|
||||
|
||||
md3Frame_t *frame = (md3Frame_t *)( (byte *)md3 + md3->ofsFrames );
|
||||
|
||||
ret.AddPoint( frame->bounds[0] );
|
||||
ret.AddPoint( frame->bounds[1] );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
146
neo/renderer/Model_md3.h
Normal file
146
neo/renderer/Model_md3.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __MODEL_MD3_H__
|
||||
#define __MODEL_MD3_H__
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
|
||||
.MD3 triangle model file format
|
||||
|
||||
Private structures used by the MD3 loader.
|
||||
|
||||
========================================================================
|
||||
*/
|
||||
|
||||
#define MD3_IDENT (('3'<<24)+('P'<<16)+('D'<<8)+'I')
|
||||
#define MD3_VERSION 15
|
||||
|
||||
// surface geometry should not exceed these limits
|
||||
#define SHADER_MAX_VERTEXES 1000
|
||||
#define SHADER_MAX_INDEXES (6*SHADER_MAX_VERTEXES)
|
||||
|
||||
// limits
|
||||
#define MD3_MAX_LODS 4
|
||||
#define MD3_MAX_TRIANGLES 8192 // per surface
|
||||
#define MD3_MAX_VERTS 4096 // per surface
|
||||
#define MD3_MAX_SHADERS 256 // per surface
|
||||
#define MD3_MAX_FRAMES 1024 // per model
|
||||
#define MD3_MAX_SURFACES 32 // per model
|
||||
#define MD3_MAX_TAGS 16 // per frame
|
||||
#define MAX_MD3PATH 64 // from quake3
|
||||
|
||||
// vertex scales
|
||||
#define MD3_XYZ_SCALE (1.0/64)
|
||||
|
||||
typedef struct md3Frame_s {
|
||||
idVec3 bounds[2];
|
||||
idVec3 localOrigin;
|
||||
float radius;
|
||||
char name[16];
|
||||
} md3Frame_t;
|
||||
|
||||
typedef struct md3Tag_s {
|
||||
char name[MAX_MD3PATH]; // tag name
|
||||
idVec3 origin;
|
||||
idVec3 axis[3];
|
||||
} md3Tag_t;
|
||||
|
||||
/*
|
||||
** md3Surface_t
|
||||
**
|
||||
** CHUNK SIZE
|
||||
** header sizeof( md3Surface_t )
|
||||
** shaders sizeof( md3Shader_t ) * numShaders
|
||||
** triangles[0] sizeof( md3Triangle_t ) * numTriangles
|
||||
** st sizeof( md3St_t ) * numVerts
|
||||
** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numFrames
|
||||
*/
|
||||
|
||||
typedef struct md3Surface_s {
|
||||
int ident; //
|
||||
|
||||
char name[MAX_MD3PATH]; // polyset name
|
||||
|
||||
int flags;
|
||||
int numFrames; // all surfaces in a model should have the same
|
||||
|
||||
int numShaders; // all surfaces in a model should have the same
|
||||
int numVerts;
|
||||
|
||||
int numTriangles;
|
||||
int ofsTriangles;
|
||||
|
||||
int ofsShaders; // offset from start of md3Surface_t
|
||||
int ofsSt; // texture coords are common for all frames
|
||||
int ofsXyzNormals; // numVerts * numFrames
|
||||
|
||||
int ofsEnd; // next surface follows
|
||||
} md3Surface_t;
|
||||
|
||||
typedef struct {
|
||||
char name[MAX_MD3PATH];
|
||||
const idMaterial * shader; // for in-game use
|
||||
} md3Shader_t;
|
||||
|
||||
typedef struct {
|
||||
int indexes[3];
|
||||
} md3Triangle_t;
|
||||
|
||||
typedef struct {
|
||||
float st[2];
|
||||
} md3St_t;
|
||||
|
||||
typedef struct {
|
||||
short xyz[3];
|
||||
short normal;
|
||||
} md3XyzNormal_t;
|
||||
|
||||
typedef struct md3Header_s {
|
||||
int ident;
|
||||
int version;
|
||||
|
||||
char name[MAX_MD3PATH]; // model name
|
||||
|
||||
int flags;
|
||||
|
||||
int numFrames;
|
||||
int numTags;
|
||||
int numSurfaces;
|
||||
|
||||
int numSkins;
|
||||
|
||||
int ofsFrames; // offset for first frame
|
||||
int ofsTags; // numFrames * numTags
|
||||
int ofsSurfaces; // first surface, others follow
|
||||
|
||||
int ofsEnd; // end of file
|
||||
} md3Header_t;
|
||||
|
||||
#endif /* !__MODEL_MD3_H__ */
|
||||
962
neo/renderer/Model_md5.cpp
Normal file
962
neo/renderer/Model_md5.cpp
Normal file
@@ -0,0 +1,962 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
#include "Model_local.h"
|
||||
|
||||
static const char *MD5_SnapshotName = "_MD5_Snapshot_";
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
|
||||
idMD5Mesh
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
static int c_numVerts = 0;
|
||||
static int c_numWeights = 0;
|
||||
static int c_numWeightJoints = 0;
|
||||
|
||||
typedef struct vertexWeight_s {
|
||||
int vert;
|
||||
int joint;
|
||||
idVec3 offset;
|
||||
float jointWeight;
|
||||
} vertexWeight_t;
|
||||
|
||||
/*
|
||||
====================
|
||||
idMD5Mesh::idMD5Mesh
|
||||
====================
|
||||
*/
|
||||
idMD5Mesh::idMD5Mesh() {
|
||||
scaledWeights = NULL;
|
||||
weightIndex = NULL;
|
||||
shader = NULL;
|
||||
numTris = 0;
|
||||
deformInfo = NULL;
|
||||
surfaceNum = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idMD5Mesh::~idMD5Mesh
|
||||
====================
|
||||
*/
|
||||
idMD5Mesh::~idMD5Mesh() {
|
||||
Mem_Free16( scaledWeights );
|
||||
Mem_Free16( weightIndex );
|
||||
if ( deformInfo ) {
|
||||
R_FreeDeformInfo( deformInfo );
|
||||
deformInfo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idMD5Mesh::ParseMesh
|
||||
====================
|
||||
*/
|
||||
void idMD5Mesh::ParseMesh( idLexer &parser, int numJoints, const idJointMat *joints ) {
|
||||
idToken token;
|
||||
idToken name;
|
||||
int num;
|
||||
int count;
|
||||
int jointnum;
|
||||
idStr shaderName;
|
||||
int i, j;
|
||||
idList<int> tris;
|
||||
idList<int> firstWeightForVertex;
|
||||
idList<int> numWeightsForVertex;
|
||||
int maxweight;
|
||||
idList<vertexWeight_t> tempWeights;
|
||||
|
||||
parser.ExpectTokenString( "{" );
|
||||
|
||||
//
|
||||
// parse name
|
||||
//
|
||||
if ( parser.CheckTokenString( "name" ) ) {
|
||||
parser.ReadToken( &name );
|
||||
}
|
||||
|
||||
//
|
||||
// parse shader
|
||||
//
|
||||
parser.ExpectTokenString( "shader" );
|
||||
|
||||
parser.ReadToken( &token );
|
||||
shaderName = token;
|
||||
|
||||
shader = declManager->FindMaterial( shaderName );
|
||||
|
||||
//
|
||||
// parse texture coordinates
|
||||
//
|
||||
parser.ExpectTokenString( "numverts" );
|
||||
count = parser.ParseInt();
|
||||
if ( count < 0 ) {
|
||||
parser.Error( "Invalid size: %s", token.c_str() );
|
||||
}
|
||||
|
||||
texCoords.SetNum( count );
|
||||
firstWeightForVertex.SetNum( count );
|
||||
numWeightsForVertex.SetNum( count );
|
||||
|
||||
numWeights = 0;
|
||||
maxweight = 0;
|
||||
for( i = 0; i < texCoords.Num(); i++ ) {
|
||||
parser.ExpectTokenString( "vert" );
|
||||
parser.ParseInt();
|
||||
|
||||
parser.Parse1DMatrix( 2, texCoords[ i ].ToFloatPtr() );
|
||||
|
||||
firstWeightForVertex[ i ] = parser.ParseInt();
|
||||
numWeightsForVertex[ i ] = parser.ParseInt();
|
||||
|
||||
if ( !numWeightsForVertex[ i ] ) {
|
||||
parser.Error( "Vertex without any joint weights." );
|
||||
}
|
||||
|
||||
numWeights += numWeightsForVertex[ i ];
|
||||
if ( numWeightsForVertex[ i ] + firstWeightForVertex[ i ] > maxweight ) {
|
||||
maxweight = numWeightsForVertex[ i ] + firstWeightForVertex[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// parse tris
|
||||
//
|
||||
parser.ExpectTokenString( "numtris" );
|
||||
count = parser.ParseInt();
|
||||
if ( count < 0 ) {
|
||||
parser.Error( "Invalid size: %d", count );
|
||||
}
|
||||
|
||||
tris.SetNum( count * 3 );
|
||||
numTris = count;
|
||||
for( i = 0; i < count; i++ ) {
|
||||
parser.ExpectTokenString( "tri" );
|
||||
parser.ParseInt();
|
||||
|
||||
tris[ i * 3 + 0 ] = parser.ParseInt();
|
||||
tris[ i * 3 + 1 ] = parser.ParseInt();
|
||||
tris[ i * 3 + 2 ] = parser.ParseInt();
|
||||
}
|
||||
|
||||
//
|
||||
// parse weights
|
||||
//
|
||||
parser.ExpectTokenString( "numweights" );
|
||||
count = parser.ParseInt();
|
||||
if ( count < 0 ) {
|
||||
parser.Error( "Invalid size: %d", count );
|
||||
}
|
||||
|
||||
if ( maxweight > count ) {
|
||||
parser.Warning( "Vertices reference out of range weights in model (%d of %d weights).", maxweight, count );
|
||||
}
|
||||
|
||||
tempWeights.SetNum( count );
|
||||
|
||||
for( i = 0; i < count; i++ ) {
|
||||
parser.ExpectTokenString( "weight" );
|
||||
parser.ParseInt();
|
||||
|
||||
jointnum = parser.ParseInt();
|
||||
if ( ( jointnum < 0 ) || ( jointnum >= numJoints ) ) {
|
||||
parser.Error( "Joint Index out of range(%d): %d", numJoints, jointnum );
|
||||
}
|
||||
|
||||
tempWeights[ i ].joint = jointnum;
|
||||
tempWeights[ i ].jointWeight = parser.ParseFloat();
|
||||
|
||||
parser.Parse1DMatrix( 3, tempWeights[ i ].offset.ToFloatPtr() );
|
||||
}
|
||||
|
||||
// create pre-scaled weights and an index for the vertex/joint lookup
|
||||
scaledWeights = (idVec4 *) Mem_Alloc16( numWeights * sizeof( scaledWeights[0] ) );
|
||||
weightIndex = (int *) Mem_Alloc16( numWeights * 2 * sizeof( weightIndex[0] ) );
|
||||
memset( weightIndex, 0, numWeights * 2 * sizeof( weightIndex[0] ) );
|
||||
|
||||
count = 0;
|
||||
for( i = 0; i < texCoords.Num(); i++ ) {
|
||||
num = firstWeightForVertex[i];
|
||||
for( j = 0; j < numWeightsForVertex[i]; j++, num++, count++ ) {
|
||||
scaledWeights[count].ToVec3() = tempWeights[num].offset * tempWeights[num].jointWeight;
|
||||
scaledWeights[count].w = tempWeights[num].jointWeight;
|
||||
weightIndex[count * 2 + 0] = tempWeights[num].joint * sizeof( idJointMat );
|
||||
}
|
||||
weightIndex[count * 2 - 1] = 1;
|
||||
}
|
||||
|
||||
tempWeights.Clear();
|
||||
numWeightsForVertex.Clear();
|
||||
firstWeightForVertex.Clear();
|
||||
|
||||
parser.ExpectTokenString( "}" );
|
||||
|
||||
// update counters
|
||||
c_numVerts += texCoords.Num();
|
||||
c_numWeights += numWeights;
|
||||
c_numWeightJoints++;
|
||||
for ( i = 0; i < numWeights; i++ ) {
|
||||
c_numWeightJoints += weightIndex[i*2+1];
|
||||
}
|
||||
|
||||
//
|
||||
// build the information that will be common to all animations of this mesh:
|
||||
// silhouette edge connectivity and normal / tangent generation information
|
||||
//
|
||||
idDrawVert *verts = (idDrawVert *) _alloca16( texCoords.Num() * sizeof( idDrawVert ) );
|
||||
for ( i = 0; i < texCoords.Num(); i++ ) {
|
||||
verts[i].Clear();
|
||||
verts[i].st = texCoords[i];
|
||||
}
|
||||
TransformVerts( verts, joints );
|
||||
deformInfo = R_BuildDeformInfo( texCoords.Num(), verts, tris.Num(), tris.Ptr(), shader->UseUnsmoothedTangents() );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idMD5Mesh::TransformVerts
|
||||
====================
|
||||
*/
|
||||
void idMD5Mesh::TransformVerts( idDrawVert *verts, const idJointMat *entJoints ) {
|
||||
SIMDProcessor->TransformVerts( verts, texCoords.Num(), entJoints, scaledWeights, weightIndex, numWeights );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idMD5Mesh::TransformScaledVerts
|
||||
|
||||
Special transform to make the mesh seem fat or skinny. May be used for zombie deaths
|
||||
====================
|
||||
*/
|
||||
void idMD5Mesh::TransformScaledVerts( idDrawVert *verts, const idJointMat *entJoints, float scale ) {
|
||||
idVec4 *scaledWeights = (idVec4 *) _alloca16( numWeights * sizeof( scaledWeights[0] ) );
|
||||
SIMDProcessor->Mul( scaledWeights[0].ToFloatPtr(), scale, scaledWeights[0].ToFloatPtr(), numWeights * 4 );
|
||||
SIMDProcessor->TransformVerts( verts, texCoords.Num(), entJoints, scaledWeights, weightIndex, numWeights );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idMD5Mesh::UpdateSurface
|
||||
====================
|
||||
*/
|
||||
void idMD5Mesh::UpdateSurface( const struct renderEntity_s *ent, const idJointMat *entJoints, modelSurface_t *surf ) {
|
||||
int i, base;
|
||||
srfTriangles_t *tri;
|
||||
|
||||
tr.pc.c_deformedSurfaces++;
|
||||
tr.pc.c_deformedVerts += deformInfo->numOutputVerts;
|
||||
tr.pc.c_deformedIndexes += deformInfo->numIndexes;
|
||||
|
||||
surf->shader = shader;
|
||||
|
||||
if ( surf->geometry ) {
|
||||
// if the number of verts and indexes are the same we can re-use the triangle surface
|
||||
// the number of indexes must be the same to assure the correct amount of memory is allocated for the facePlanes
|
||||
if ( surf->geometry->numVerts == deformInfo->numOutputVerts && surf->geometry->numIndexes == deformInfo->numIndexes ) {
|
||||
R_FreeStaticTriSurfVertexCaches( surf->geometry );
|
||||
} else {
|
||||
R_FreeStaticTriSurf( surf->geometry );
|
||||
surf->geometry = R_AllocStaticTriSurf();
|
||||
}
|
||||
} else {
|
||||
surf->geometry = R_AllocStaticTriSurf();
|
||||
}
|
||||
|
||||
tri = surf->geometry;
|
||||
|
||||
// note that some of the data is references, and should not be freed
|
||||
tri->deformedSurface = true;
|
||||
tri->tangentsCalculated = false;
|
||||
tri->facePlanesCalculated = false;
|
||||
|
||||
tri->numIndexes = deformInfo->numIndexes;
|
||||
tri->indexes = deformInfo->indexes;
|
||||
tri->silIndexes = deformInfo->silIndexes;
|
||||
tri->numMirroredVerts = deformInfo->numMirroredVerts;
|
||||
tri->mirroredVerts = deformInfo->mirroredVerts;
|
||||
tri->numDupVerts = deformInfo->numDupVerts;
|
||||
tri->dupVerts = deformInfo->dupVerts;
|
||||
tri->numSilEdges = deformInfo->numSilEdges;
|
||||
tri->silEdges = deformInfo->silEdges;
|
||||
tri->dominantTris = deformInfo->dominantTris;
|
||||
tri->numVerts = deformInfo->numOutputVerts;
|
||||
|
||||
if ( tri->verts == NULL ) {
|
||||
R_AllocStaticTriSurfVerts( tri, tri->numVerts );
|
||||
for ( i = 0; i < deformInfo->numSourceVerts; i++ ) {
|
||||
tri->verts[i].Clear();
|
||||
tri->verts[i].st = texCoords[i];
|
||||
}
|
||||
}
|
||||
|
||||
if ( ent->shaderParms[ SHADERPARM_MD5_SKINSCALE ] != 0.0f ) {
|
||||
TransformScaledVerts( tri->verts, entJoints, ent->shaderParms[ SHADERPARM_MD5_SKINSCALE ] );
|
||||
} else {
|
||||
TransformVerts( tri->verts, entJoints );
|
||||
}
|
||||
|
||||
// replicate the mirror seam vertexes
|
||||
base = deformInfo->numOutputVerts - deformInfo->numMirroredVerts;
|
||||
for ( i = 0; i < deformInfo->numMirroredVerts; i++ ) {
|
||||
tri->verts[base + i] = tri->verts[deformInfo->mirroredVerts[i]];
|
||||
}
|
||||
|
||||
R_BoundTriSurf( tri );
|
||||
|
||||
// If a surface is going to be have a lighting interaction generated, it will also have to call
|
||||
// R_DeriveTangents() to get normals, tangents, and face planes. If it only
|
||||
// needs shadows generated, it will only have to generate face planes. If it only
|
||||
// has ambient drawing, or is culled, no additional work will be necessary
|
||||
if ( !r_useDeferredTangents.GetBool() ) {
|
||||
// set face planes, vertex normals, tangents
|
||||
R_DeriveTangents( tri );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idMD5Mesh::CalcBounds
|
||||
====================
|
||||
*/
|
||||
idBounds idMD5Mesh::CalcBounds( const idJointMat *entJoints ) {
|
||||
idBounds bounds;
|
||||
idDrawVert *verts = (idDrawVert *) _alloca16( texCoords.Num() * sizeof( idDrawVert ) );
|
||||
|
||||
TransformVerts( verts, entJoints );
|
||||
|
||||
SIMDProcessor->MinMax( bounds[0], bounds[1], verts, texCoords.Num() );
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idMD5Mesh::NearestJoint
|
||||
====================
|
||||
*/
|
||||
int idMD5Mesh::NearestJoint( int a, int b, int c ) const {
|
||||
int i, bestJoint, vertNum, weightVertNum;
|
||||
float bestWeight;
|
||||
|
||||
// duplicated vertices might not have weights
|
||||
if ( a >= 0 && a < texCoords.Num() ) {
|
||||
vertNum = a;
|
||||
} else if ( b >= 0 && b < texCoords.Num() ) {
|
||||
vertNum = b;
|
||||
} else if ( c >= 0 && c < texCoords.Num() ) {
|
||||
vertNum = c;
|
||||
} else {
|
||||
// all vertices are duplicates which shouldn't happen
|
||||
return 0;
|
||||
}
|
||||
|
||||
// find the first weight for this vertex
|
||||
weightVertNum = 0;
|
||||
for( i = 0; weightVertNum < vertNum; i++ ) {
|
||||
weightVertNum += weightIndex[i*2+1];
|
||||
}
|
||||
|
||||
// get the joint for the largest weight
|
||||
bestWeight = scaledWeights[i].w;
|
||||
bestJoint = weightIndex[i*2+0] / sizeof( idJointMat );
|
||||
for( ; weightIndex[i*2+1] == 0; i++ ) {
|
||||
if ( scaledWeights[i].w > bestWeight ) {
|
||||
bestWeight = scaledWeights[i].w;
|
||||
bestJoint = weightIndex[i*2+0] / sizeof( idJointMat );
|
||||
}
|
||||
}
|
||||
return bestJoint;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idMD5Mesh::NumVerts
|
||||
====================
|
||||
*/
|
||||
int idMD5Mesh::NumVerts( void ) const {
|
||||
return texCoords.Num();
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idMD5Mesh::NumTris
|
||||
====================
|
||||
*/
|
||||
int idMD5Mesh::NumTris( void ) const {
|
||||
return numTris;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idMD5Mesh::NumWeights
|
||||
====================
|
||||
*/
|
||||
int idMD5Mesh::NumWeights( void ) const {
|
||||
return numWeights;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
||||
idRenderModelMD5
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::ParseJoint
|
||||
====================
|
||||
*/
|
||||
void idRenderModelMD5::ParseJoint( idLexer &parser, idMD5Joint *joint, idJointQuat *defaultPose ) {
|
||||
idToken token;
|
||||
int num;
|
||||
|
||||
//
|
||||
// parse name
|
||||
//
|
||||
parser.ReadToken( &token );
|
||||
joint->name = token;
|
||||
|
||||
//
|
||||
// parse parent
|
||||
//
|
||||
num = parser.ParseInt();
|
||||
if ( num < 0 ) {
|
||||
joint->parent = NULL;
|
||||
} else {
|
||||
if ( num >= joints.Num() - 1 ) {
|
||||
parser.Error( "Invalid parent for joint '%s'", joint->name.c_str() );
|
||||
}
|
||||
joint->parent = &joints[ num ];
|
||||
}
|
||||
|
||||
//
|
||||
// parse default pose
|
||||
//
|
||||
parser.Parse1DMatrix( 3, defaultPose->t.ToFloatPtr() );
|
||||
parser.Parse1DMatrix( 3, defaultPose->q.ToFloatPtr() );
|
||||
defaultPose->q.w = defaultPose->q.CalcW();
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::InitFromFile
|
||||
====================
|
||||
*/
|
||||
void idRenderModelMD5::InitFromFile( const char *fileName ) {
|
||||
name = fileName;
|
||||
LoadModel();
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::LoadModel
|
||||
|
||||
used for initial loads, reloadModel, and reloading the data of purged models
|
||||
Upon exit, the model will absolutely be valid, but possibly as a default model
|
||||
====================
|
||||
*/
|
||||
void idRenderModelMD5::LoadModel() {
|
||||
int version;
|
||||
int i;
|
||||
int num;
|
||||
int parentNum;
|
||||
idToken token;
|
||||
idLexer parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS );
|
||||
idJointQuat *pose;
|
||||
idMD5Joint *joint;
|
||||
idJointMat *poseMat3;
|
||||
|
||||
if ( !purged ) {
|
||||
PurgeModel();
|
||||
}
|
||||
purged = false;
|
||||
|
||||
if ( !parser.LoadFile( name ) ) {
|
||||
MakeDefaultModel();
|
||||
return;
|
||||
}
|
||||
|
||||
parser.ExpectTokenString( MD5_VERSION_STRING );
|
||||
version = parser.ParseInt();
|
||||
|
||||
if ( version != MD5_VERSION ) {
|
||||
parser.Error( "Invalid version %d. Should be version %d\n", version, MD5_VERSION );
|
||||
}
|
||||
|
||||
//
|
||||
// skip commandline
|
||||
//
|
||||
parser.ExpectTokenString( "commandline" );
|
||||
parser.ReadToken( &token );
|
||||
|
||||
// parse num joints
|
||||
parser.ExpectTokenString( "numJoints" );
|
||||
num = parser.ParseInt();
|
||||
joints.SetGranularity( 1 );
|
||||
joints.SetNum( num );
|
||||
defaultPose.SetGranularity( 1 );
|
||||
defaultPose.SetNum( num );
|
||||
poseMat3 = ( idJointMat * )_alloca16( num * sizeof( *poseMat3 ) );
|
||||
|
||||
// parse num meshes
|
||||
parser.ExpectTokenString( "numMeshes" );
|
||||
num = parser.ParseInt();
|
||||
if ( num < 0 ) {
|
||||
parser.Error( "Invalid size: %d", num );
|
||||
}
|
||||
meshes.SetGranularity( 1 );
|
||||
meshes.SetNum( num );
|
||||
|
||||
//
|
||||
// parse joints
|
||||
//
|
||||
parser.ExpectTokenString( "joints" );
|
||||
parser.ExpectTokenString( "{" );
|
||||
pose = defaultPose.Ptr();
|
||||
joint = joints.Ptr();
|
||||
for( i = 0; i < joints.Num(); i++, joint++, pose++ ) {
|
||||
ParseJoint( parser, joint, pose );
|
||||
poseMat3[ i ].SetRotation( pose->q.ToMat3() );
|
||||
poseMat3[ i ].SetTranslation( pose->t );
|
||||
if ( joint->parent ) {
|
||||
parentNum = joint->parent - joints.Ptr();
|
||||
pose->q = ( poseMat3[ i ].ToMat3() * poseMat3[ parentNum ].ToMat3().Transpose() ).ToQuat();
|
||||
pose->t = ( poseMat3[ i ].ToVec3() - poseMat3[ parentNum ].ToVec3() ) * poseMat3[ parentNum ].ToMat3().Transpose();
|
||||
}
|
||||
}
|
||||
parser.ExpectTokenString( "}" );
|
||||
|
||||
for( i = 0; i < meshes.Num(); i++ ) {
|
||||
parser.ExpectTokenString( "mesh" );
|
||||
meshes[ i ].ParseMesh( parser, defaultPose.Num(), poseMat3 );
|
||||
}
|
||||
|
||||
//
|
||||
// calculate the bounds of the model
|
||||
//
|
||||
CalculateBounds( poseMat3 );
|
||||
|
||||
// set the timestamp for reloadmodels
|
||||
fileSystem->ReadFile( name, NULL, &timeStamp );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
idRenderModelMD5::Print
|
||||
==============
|
||||
*/
|
||||
void idRenderModelMD5::Print() const {
|
||||
const idMD5Mesh *mesh;
|
||||
int i;
|
||||
|
||||
common->Printf( "%s\n", name.c_str() );
|
||||
common->Printf( "Dynamic model.\n" );
|
||||
common->Printf( "Generated smooth normals.\n" );
|
||||
common->Printf( " verts tris weights material\n" );
|
||||
int totalVerts = 0;
|
||||
int totalTris = 0;
|
||||
int totalWeights = 0;
|
||||
for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
|
||||
totalVerts += mesh->NumVerts();
|
||||
totalTris += mesh->NumTris();
|
||||
totalWeights += mesh->NumWeights();
|
||||
common->Printf( "%2i: %5i %5i %7i %s\n", i, mesh->NumVerts(), mesh->NumTris(), mesh->NumWeights(), mesh->shader->GetName() );
|
||||
}
|
||||
common->Printf( "-----\n" );
|
||||
common->Printf( "%4i verts.\n", totalVerts );
|
||||
common->Printf( "%4i tris.\n", totalTris );
|
||||
common->Printf( "%4i weights.\n", totalWeights );
|
||||
common->Printf( "%4i joints.\n", joints.Num() );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
idRenderModelMD5::List
|
||||
==============
|
||||
*/
|
||||
void idRenderModelMD5::List() const {
|
||||
int i;
|
||||
const idMD5Mesh *mesh;
|
||||
int totalTris = 0;
|
||||
int totalVerts = 0;
|
||||
|
||||
for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
|
||||
totalTris += mesh->numTris;
|
||||
totalVerts += mesh->NumVerts();
|
||||
}
|
||||
common->Printf( " %4ik %3i %4i %4i %s(MD5)", Memory()/1024, meshes.Num(), totalVerts, totalTris, Name() );
|
||||
|
||||
if ( defaulted ) {
|
||||
common->Printf( " (DEFAULTED)" );
|
||||
}
|
||||
|
||||
common->Printf( "\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::CalculateBounds
|
||||
====================
|
||||
*/
|
||||
void idRenderModelMD5::CalculateBounds( const idJointMat *entJoints ) {
|
||||
int i;
|
||||
idMD5Mesh *mesh;
|
||||
|
||||
bounds.Clear();
|
||||
for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
|
||||
bounds.AddBounds( mesh->CalcBounds( entJoints ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::Bounds
|
||||
|
||||
This calculates a rough bounds by using the joint radii without
|
||||
transforming all the points
|
||||
====================
|
||||
*/
|
||||
idBounds idRenderModelMD5::Bounds( const renderEntity_t *ent ) const {
|
||||
#if 0
|
||||
// we can't calculate a rational bounds without an entity,
|
||||
// because joints could be positioned to deform it into an
|
||||
// arbitrarily large shape
|
||||
if ( !ent ) {
|
||||
common->Error( "idRenderModelMD5::Bounds: called without entity" );
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( !ent ) {
|
||||
// this is the bounds for the reference pose
|
||||
return bounds;
|
||||
}
|
||||
|
||||
return ent->bounds;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::DrawJoints
|
||||
====================
|
||||
*/
|
||||
void idRenderModelMD5::DrawJoints( const renderEntity_t *ent, const struct viewDef_s *view ) const {
|
||||
int i;
|
||||
int num;
|
||||
idVec3 pos;
|
||||
const idJointMat *joint;
|
||||
const idMD5Joint *md5Joint;
|
||||
int parentNum;
|
||||
|
||||
num = ent->numJoints;
|
||||
joint = ent->joints;
|
||||
md5Joint = joints.Ptr();
|
||||
for( i = 0; i < num; i++, joint++, md5Joint++ ) {
|
||||
pos = ent->origin + joint->ToVec3() * ent->axis;
|
||||
if ( md5Joint->parent ) {
|
||||
parentNum = md5Joint->parent - joints.Ptr();
|
||||
session->rw->DebugLine( colorWhite, ent->origin + ent->joints[ parentNum ].ToVec3() * ent->axis, pos );
|
||||
}
|
||||
|
||||
session->rw->DebugLine( colorRed, pos, pos + joint->ToMat3()[ 0 ] * 2.0f * ent->axis );
|
||||
session->rw->DebugLine( colorGreen, pos, pos + joint->ToMat3()[ 1 ] * 2.0f * ent->axis );
|
||||
session->rw->DebugLine( colorBlue, pos, pos + joint->ToMat3()[ 2 ] * 2.0f * ent->axis );
|
||||
}
|
||||
|
||||
idBounds bounds;
|
||||
|
||||
bounds.FromTransformedBounds( ent->bounds, vec3_zero, ent->axis );
|
||||
session->rw->DebugBounds( colorMagenta, bounds, ent->origin );
|
||||
|
||||
if ( ( r_jointNameScale.GetFloat() != 0.0f ) && ( bounds.Expand( 128.0f ).ContainsPoint( view->renderView.vieworg - ent->origin ) ) ) {
|
||||
idVec3 offset( 0, 0, r_jointNameOffset.GetFloat() );
|
||||
float scale;
|
||||
|
||||
scale = r_jointNameScale.GetFloat();
|
||||
joint = ent->joints;
|
||||
num = ent->numJoints;
|
||||
for( i = 0; i < num; i++, joint++ ) {
|
||||
pos = ent->origin + joint->ToVec3() * ent->axis;
|
||||
session->rw->DrawText( joints[ i ].name, pos + offset, scale, colorWhite, view->renderView.viewaxis, 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::InstantiateDynamicModel
|
||||
====================
|
||||
*/
|
||||
idRenderModel *idRenderModelMD5::InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) {
|
||||
int i, surfaceNum;
|
||||
idMD5Mesh *mesh;
|
||||
idRenderModelStatic *staticModel;
|
||||
|
||||
if ( cachedModel && !r_useCachedDynamicModels.GetBool() ) {
|
||||
delete cachedModel;
|
||||
cachedModel = NULL;
|
||||
}
|
||||
|
||||
if ( purged ) {
|
||||
common->DWarning( "model %s instantiated while purged", Name() );
|
||||
LoadModel();
|
||||
}
|
||||
|
||||
if ( !ent->joints ) {
|
||||
common->Printf( "idRenderModelMD5::InstantiateDynamicModel: NULL joints on renderEntity for '%s'\n", Name() );
|
||||
delete cachedModel;
|
||||
return NULL;
|
||||
} else if ( ent->numJoints != joints.Num() ) {
|
||||
common->Printf( "idRenderModelMD5::InstantiateDynamicModel: renderEntity has different number of joints than model for '%s'\n", Name() );
|
||||
delete cachedModel;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tr.pc.c_generateMd5++;
|
||||
|
||||
if ( cachedModel ) {
|
||||
assert( dynamic_cast<idRenderModelStatic *>(cachedModel) != NULL );
|
||||
assert( idStr::Icmp( cachedModel->Name(), MD5_SnapshotName ) == 0 );
|
||||
staticModel = static_cast<idRenderModelStatic *>(cachedModel);
|
||||
} else {
|
||||
staticModel = new idRenderModelStatic;
|
||||
staticModel->InitEmpty( MD5_SnapshotName );
|
||||
}
|
||||
|
||||
staticModel->bounds.Clear();
|
||||
|
||||
if ( r_showSkel.GetInteger() ) {
|
||||
if ( ( view != NULL ) && ( !r_skipSuppress.GetBool() || !ent->suppressSurfaceInViewID || ( ent->suppressSurfaceInViewID != view->renderView.viewID ) ) ) {
|
||||
// only draw the skeleton
|
||||
DrawJoints( ent, view );
|
||||
}
|
||||
|
||||
if ( r_showSkel.GetInteger() > 1 ) {
|
||||
// turn off the model when showing the skeleton
|
||||
staticModel->InitEmpty( MD5_SnapshotName );
|
||||
return staticModel;
|
||||
}
|
||||
}
|
||||
|
||||
// create all the surfaces
|
||||
for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
|
||||
// avoid deforming the surface if it will be a nodraw due to a skin remapping
|
||||
// FIXME: may have to still deform clipping hulls
|
||||
const idMaterial *shader = mesh->shader;
|
||||
|
||||
shader = R_RemapShaderBySkin( shader, ent->customSkin, ent->customShader );
|
||||
|
||||
if ( !shader || ( !shader->IsDrawn() && !shader->SurfaceCastsShadow() ) ) {
|
||||
staticModel->DeleteSurfaceWithId( i );
|
||||
mesh->surfaceNum = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
modelSurface_t *surf;
|
||||
|
||||
if ( staticModel->FindSurfaceWithId( i, surfaceNum ) ) {
|
||||
mesh->surfaceNum = surfaceNum;
|
||||
surf = &staticModel->surfaces[surfaceNum];
|
||||
} else {
|
||||
|
||||
// Remove Overlays before adding new surfaces
|
||||
idRenderModelOverlay::RemoveOverlaySurfacesFromModel( staticModel );
|
||||
|
||||
mesh->surfaceNum = staticModel->NumSurfaces();
|
||||
surf = &staticModel->surfaces.Alloc();
|
||||
surf->geometry = NULL;
|
||||
surf->shader = NULL;
|
||||
surf->id = i;
|
||||
}
|
||||
|
||||
mesh->UpdateSurface( ent, ent->joints, surf );
|
||||
|
||||
staticModel->bounds.AddPoint( surf->geometry->bounds[0] );
|
||||
staticModel->bounds.AddPoint( surf->geometry->bounds[1] );
|
||||
}
|
||||
|
||||
return staticModel;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::IsDynamicModel
|
||||
====================
|
||||
*/
|
||||
dynamicModel_t idRenderModelMD5::IsDynamicModel() const {
|
||||
return DM_CACHED;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::NumJoints
|
||||
====================
|
||||
*/
|
||||
int idRenderModelMD5::NumJoints( void ) const {
|
||||
return joints.Num();
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::GetJoints
|
||||
====================
|
||||
*/
|
||||
const idMD5Joint *idRenderModelMD5::GetJoints( void ) const {
|
||||
return joints.Ptr();
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::GetDefaultPose
|
||||
====================
|
||||
*/
|
||||
const idJointQuat *idRenderModelMD5::GetDefaultPose( void ) const {
|
||||
return defaultPose.Ptr();
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::GetJointHandle
|
||||
====================
|
||||
*/
|
||||
jointHandle_t idRenderModelMD5::GetJointHandle( const char *name ) const {
|
||||
const idMD5Joint *joint;
|
||||
int i;
|
||||
|
||||
joint = joints.Ptr();
|
||||
for( i = 0; i < joints.Num(); i++, joint++ ) {
|
||||
if ( idStr::Icmp( joint->name.c_str(), name ) == 0 ) {
|
||||
return ( jointHandle_t )i;
|
||||
}
|
||||
}
|
||||
|
||||
return INVALID_JOINT;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
idRenderModelMD5::GetJointName
|
||||
=====================
|
||||
*/
|
||||
const char *idRenderModelMD5::GetJointName( jointHandle_t handle ) const {
|
||||
if ( ( handle < 0 ) || ( handle >= joints.Num() ) ) {
|
||||
return "<invalid joint>";
|
||||
}
|
||||
|
||||
return joints[ handle ].name;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::NearestJoint
|
||||
====================
|
||||
*/
|
||||
int idRenderModelMD5::NearestJoint( int surfaceNum, int a, int b, int c ) const {
|
||||
int i;
|
||||
const idMD5Mesh *mesh;
|
||||
|
||||
if ( surfaceNum > meshes.Num() ) {
|
||||
common->Error( "idRenderModelMD5::NearestJoint: surfaceNum > meshes.Num()" );
|
||||
}
|
||||
|
||||
for ( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
|
||||
if ( mesh->surfaceNum == surfaceNum ) {
|
||||
return mesh->NearestJoint( a, b, c );
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelMD5::TouchData
|
||||
|
||||
models that are already loaded at level start time
|
||||
will still touch their materials to make sure they
|
||||
are kept loaded
|
||||
====================
|
||||
*/
|
||||
void idRenderModelMD5::TouchData() {
|
||||
idMD5Mesh *mesh;
|
||||
int i;
|
||||
|
||||
for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
|
||||
declManager->FindMaterial( mesh->shader->GetName() );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
idRenderModelMD5::PurgeModel
|
||||
|
||||
frees all the data, but leaves the class around for dangling references,
|
||||
which can regenerate the data with LoadModel()
|
||||
===================
|
||||
*/
|
||||
void idRenderModelMD5::PurgeModel() {
|
||||
purged = true;
|
||||
joints.Clear();
|
||||
defaultPose.Clear();
|
||||
meshes.Clear();
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
idRenderModelMD5::Memory
|
||||
===================
|
||||
*/
|
||||
int idRenderModelMD5::Memory() const {
|
||||
int total, i;
|
||||
|
||||
total = sizeof( *this );
|
||||
total += joints.MemoryUsed() + defaultPose.MemoryUsed() + meshes.MemoryUsed();
|
||||
|
||||
// count up strings
|
||||
for ( i = 0; i < joints.Num(); i++ ) {
|
||||
total += joints[i].name.DynamicMemoryUsed();
|
||||
}
|
||||
|
||||
// count up meshes
|
||||
for ( i = 0 ; i < meshes.Num() ; i++ ) {
|
||||
const idMD5Mesh *mesh = &meshes[i];
|
||||
|
||||
total += mesh->texCoords.MemoryUsed() + mesh->numWeights * ( sizeof( mesh->scaledWeights[0] ) + sizeof( mesh->weightIndex[0] ) * 2 );
|
||||
|
||||
// sum up deform info
|
||||
total += sizeof( mesh->deformInfo );
|
||||
total += R_DeformInfoMemoryUsed( mesh->deformInfo );
|
||||
}
|
||||
return total;
|
||||
}
|
||||
290
neo/renderer/Model_prt.cpp
Normal file
290
neo/renderer/Model_prt.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
#include "Model_local.h"
|
||||
|
||||
static const char *parametricParticle_SnapshotName = "_ParametricParticle_Snapshot_";
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelPrt::idRenderModelPrt
|
||||
====================
|
||||
*/
|
||||
idRenderModelPrt::idRenderModelPrt() {
|
||||
particleSystem = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelPrt::InitFromFile
|
||||
====================
|
||||
*/
|
||||
void idRenderModelPrt::InitFromFile( const char *fileName ) {
|
||||
name = fileName;
|
||||
particleSystem = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, fileName ) );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderModelPrt::TouchData
|
||||
=================
|
||||
*/
|
||||
void idRenderModelPrt::TouchData( void ) {
|
||||
// Ensure our particle system is added to the list of referenced decls
|
||||
particleSystem = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, name ) );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelPrt::InstantiateDynamicModel
|
||||
====================
|
||||
*/
|
||||
idRenderModel *idRenderModelPrt::InstantiateDynamicModel( const struct renderEntity_s *renderEntity, const struct viewDef_s *viewDef, idRenderModel *cachedModel ) {
|
||||
idRenderModelStatic *staticModel;
|
||||
|
||||
if ( cachedModel && !r_useCachedDynamicModels.GetBool() ) {
|
||||
delete cachedModel;
|
||||
cachedModel = NULL;
|
||||
}
|
||||
|
||||
// this may be triggered by a model trace or other non-view related source, to which we should look like an empty model
|
||||
if ( renderEntity == NULL || viewDef == NULL ) {
|
||||
delete cachedModel;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( r_skipParticles.GetBool() ) {
|
||||
delete cachedModel;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
// if the entire system has faded out
|
||||
if ( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] && viewDef->renderView.time * 0.001f >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] ) {
|
||||
delete cachedModel;
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
|
||||
if ( cachedModel != NULL ) {
|
||||
|
||||
assert( dynamic_cast<idRenderModelStatic *>(cachedModel) != NULL );
|
||||
assert( idStr::Icmp( cachedModel->Name(), parametricParticle_SnapshotName ) == 0 );
|
||||
|
||||
staticModel = static_cast<idRenderModelStatic *>(cachedModel);
|
||||
|
||||
} else {
|
||||
|
||||
staticModel = new idRenderModelStatic;
|
||||
staticModel->InitEmpty( parametricParticle_SnapshotName );
|
||||
}
|
||||
|
||||
particleGen_t g;
|
||||
|
||||
g.renderEnt = renderEntity;
|
||||
g.renderView = &viewDef->renderView;
|
||||
g.origin.Zero();
|
||||
g.axis.Identity();
|
||||
|
||||
for ( int stageNum = 0; stageNum < particleSystem->stages.Num(); stageNum++ ) {
|
||||
idParticleStage *stage = particleSystem->stages[stageNum];
|
||||
|
||||
if ( !stage->material ) {
|
||||
continue;
|
||||
}
|
||||
if ( !stage->cycleMsec ) {
|
||||
continue;
|
||||
}
|
||||
if ( stage->hidden ) { // just for gui particle editor use
|
||||
staticModel->DeleteSurfaceWithId( stageNum );
|
||||
continue;
|
||||
}
|
||||
|
||||
idRandom steppingRandom, steppingRandom2;
|
||||
|
||||
int stageAge = g.renderView->time + renderEntity->shaderParms[SHADERPARM_TIMEOFFSET] * 1000 - stage->timeOffset * 1000;
|
||||
int stageCycle = stageAge / stage->cycleMsec;
|
||||
int inCycleTime = stageAge - stageCycle * stage->cycleMsec;
|
||||
|
||||
// some particles will be in this cycle, some will be in the previous cycle
|
||||
steppingRandom.SetSeed( (( stageCycle << 10 ) & idRandom::MAX_RAND) ^ (int)( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) );
|
||||
steppingRandom2.SetSeed( (( (stageCycle-1) << 10 ) & idRandom::MAX_RAND) ^ (int)( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) );
|
||||
|
||||
int count = stage->totalParticles * stage->NumQuadsPerParticle();
|
||||
|
||||
int surfaceNum;
|
||||
modelSurface_t *surf;
|
||||
|
||||
if ( staticModel->FindSurfaceWithId( stageNum, surfaceNum ) ) {
|
||||
surf = &staticModel->surfaces[surfaceNum];
|
||||
R_FreeStaticTriSurfVertexCaches( surf->geometry );
|
||||
} else {
|
||||
surf = &staticModel->surfaces.Alloc();
|
||||
surf->id = stageNum;
|
||||
surf->shader = stage->material;
|
||||
surf->geometry = R_AllocStaticTriSurf();
|
||||
R_AllocStaticTriSurfVerts( surf->geometry, 4 * count );
|
||||
R_AllocStaticTriSurfIndexes( surf->geometry, 6 * count );
|
||||
R_AllocStaticTriSurfPlanes( surf->geometry, 6 * count );
|
||||
}
|
||||
|
||||
int numVerts = 0;
|
||||
idDrawVert *verts = surf->geometry->verts;
|
||||
|
||||
for ( int index = 0; index < stage->totalParticles; index++ ) {
|
||||
g.index = index;
|
||||
|
||||
// bump the random
|
||||
steppingRandom.RandomInt();
|
||||
steppingRandom2.RandomInt();
|
||||
|
||||
// calculate local age for this index
|
||||
int bunchOffset = stage->particleLife * 1000 * stage->spawnBunching * index / stage->totalParticles;
|
||||
|
||||
int particleAge = stageAge - bunchOffset;
|
||||
int particleCycle = particleAge / stage->cycleMsec;
|
||||
if ( particleCycle < 0 ) {
|
||||
// before the particleSystem spawned
|
||||
continue;
|
||||
}
|
||||
if ( stage->cycles && particleCycle >= stage->cycles ) {
|
||||
// cycled systems will only run cycle times
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( particleCycle == stageCycle ) {
|
||||
g.random = steppingRandom;
|
||||
} else {
|
||||
g.random = steppingRandom2;
|
||||
}
|
||||
|
||||
int inCycleTime = particleAge - particleCycle * stage->cycleMsec;
|
||||
|
||||
if ( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] &&
|
||||
g.renderView->time - inCycleTime >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME]*1000 ) {
|
||||
// don't fire any more particles
|
||||
continue;
|
||||
}
|
||||
|
||||
// supress particles before or after the age clamp
|
||||
g.frac = (float)inCycleTime / ( stage->particleLife * 1000 );
|
||||
if ( g.frac < 0.0f ) {
|
||||
// yet to be spawned
|
||||
continue;
|
||||
}
|
||||
if ( g.frac > 1.0f ) {
|
||||
// this particle is in the deadTime band
|
||||
continue;
|
||||
}
|
||||
|
||||
// this is needed so aimed particles can calculate origins at different times
|
||||
g.originalRandom = g.random;
|
||||
|
||||
g.age = g.frac * stage->particleLife;
|
||||
|
||||
// if the particle doesn't get drawn because it is faded out or beyond a kill region, don't increment the verts
|
||||
numVerts += stage->CreateParticle( &g, verts + numVerts );
|
||||
}
|
||||
|
||||
// numVerts must be a multiple of 4
|
||||
assert( ( numVerts & 3 ) == 0 && numVerts <= 4 * count );
|
||||
|
||||
// build the indexes
|
||||
int numIndexes = 0;
|
||||
glIndex_t *indexes = surf->geometry->indexes;
|
||||
for ( int i = 0; i < numVerts; i += 4 ) {
|
||||
indexes[numIndexes+0] = i;
|
||||
indexes[numIndexes+1] = i+2;
|
||||
indexes[numIndexes+2] = i+3;
|
||||
indexes[numIndexes+3] = i;
|
||||
indexes[numIndexes+4] = i+3;
|
||||
indexes[numIndexes+5] = i+1;
|
||||
numIndexes += 6;
|
||||
}
|
||||
|
||||
surf->geometry->tangentsCalculated = false;
|
||||
surf->geometry->facePlanesCalculated = false;
|
||||
surf->geometry->numVerts = numVerts;
|
||||
surf->geometry->numIndexes = numIndexes;
|
||||
surf->geometry->bounds = stage->bounds; // just always draw the particles
|
||||
}
|
||||
|
||||
return staticModel;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelPrt::IsDynamicModel
|
||||
====================
|
||||
*/
|
||||
dynamicModel_t idRenderModelPrt::IsDynamicModel() const {
|
||||
return DM_CONTINUOUS;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelPrt::Bounds
|
||||
====================
|
||||
*/
|
||||
idBounds idRenderModelPrt::Bounds( const struct renderEntity_s *ent ) const {
|
||||
return particleSystem->bounds;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelPrt::DepthHack
|
||||
====================
|
||||
*/
|
||||
float idRenderModelPrt::DepthHack() const {
|
||||
return particleSystem->depthHack;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelPrt::Memory
|
||||
====================
|
||||
*/
|
||||
int idRenderModelPrt::Memory() const {
|
||||
int total = 0;
|
||||
|
||||
total += idRenderModelStatic::Memory();
|
||||
|
||||
if ( particleSystem ) {
|
||||
total += sizeof( *particleSystem );
|
||||
|
||||
for ( int i = 0; i < particleSystem->stages.Num(); i++ ) {
|
||||
total += sizeof( particleSystem->stages[i] );
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
198
neo/renderer/Model_sprite.cpp
Normal file
198
neo/renderer/Model_sprite.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
#include "Model_local.h"
|
||||
|
||||
|
||||
/*
|
||||
|
||||
A simple sprite model that always faces the view axis.
|
||||
|
||||
*/
|
||||
|
||||
static const char *sprite_SnapshotName = "_sprite_Snapshot_";
|
||||
|
||||
/*
|
||||
===============
|
||||
idRenderModelBeam::IsDynamicModel
|
||||
===============
|
||||
*/
|
||||
dynamicModel_t idRenderModelSprite::IsDynamicModel() const {
|
||||
return DM_CONTINUOUS;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
idRenderModelBeam::IsLoaded
|
||||
===============
|
||||
*/
|
||||
bool idRenderModelSprite::IsLoaded() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
idRenderModelSprite::InstantiateDynamicModel
|
||||
===============
|
||||
*/
|
||||
idRenderModel * idRenderModelSprite::InstantiateDynamicModel( const struct renderEntity_s *renderEntity, const struct viewDef_s *viewDef, idRenderModel *cachedModel ) {
|
||||
idRenderModelStatic *staticModel;
|
||||
srfTriangles_t *tri;
|
||||
modelSurface_t surf;
|
||||
|
||||
if ( cachedModel && !r_useCachedDynamicModels.GetBool() ) {
|
||||
delete cachedModel;
|
||||
cachedModel = NULL;
|
||||
}
|
||||
|
||||
if ( renderEntity == NULL || viewDef == NULL ) {
|
||||
delete cachedModel;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( cachedModel != NULL ) {
|
||||
|
||||
assert( dynamic_cast<idRenderModelStatic *>( cachedModel ) != NULL );
|
||||
assert( idStr::Icmp( cachedModel->Name(), sprite_SnapshotName ) == 0 );
|
||||
|
||||
staticModel = static_cast<idRenderModelStatic *>( cachedModel );
|
||||
surf = *staticModel->Surface( 0 );
|
||||
tri = surf.geometry;
|
||||
|
||||
} else {
|
||||
|
||||
staticModel = new idRenderModelStatic;
|
||||
staticModel->InitEmpty( sprite_SnapshotName );
|
||||
|
||||
tri = R_AllocStaticTriSurf();
|
||||
R_AllocStaticTriSurfVerts( tri, 4 );
|
||||
R_AllocStaticTriSurfIndexes( tri, 6 );
|
||||
|
||||
tri->verts[ 0 ].Clear();
|
||||
tri->verts[ 0 ].normal.Set( 1.0f, 0.0f, 0.0f );
|
||||
tri->verts[ 0 ].tangents[0].Set( 0.0f, 1.0f, 0.0f );
|
||||
tri->verts[ 0 ].tangents[1].Set( 0.0f, 0.0f, 1.0f );
|
||||
tri->verts[ 0 ].st[ 0 ] = 0.0f;
|
||||
tri->verts[ 0 ].st[ 1 ] = 0.0f;
|
||||
|
||||
tri->verts[ 1 ].Clear();
|
||||
tri->verts[ 1 ].normal.Set( 1.0f, 0.0f, 0.0f );
|
||||
tri->verts[ 1 ].tangents[0].Set( 0.0f, 1.0f, 0.0f );
|
||||
tri->verts[ 1 ].tangents[1].Set( 0.0f, 0.0f, 1.0f );
|
||||
tri->verts[ 1 ].st[ 0 ] = 1.0f;
|
||||
tri->verts[ 1 ].st[ 1 ] = 0.0f;
|
||||
|
||||
tri->verts[ 2 ].Clear();
|
||||
tri->verts[ 2 ].normal.Set( 1.0f, 0.0f, 0.0f );
|
||||
tri->verts[ 2 ].tangents[0].Set( 0.0f, 1.0f, 0.0f );
|
||||
tri->verts[ 2 ].tangents[1].Set( 0.0f, 0.0f, 1.0f );
|
||||
tri->verts[ 2 ].st[ 0 ] = 1.0f;
|
||||
tri->verts[ 2 ].st[ 1 ] = 1.0f;
|
||||
|
||||
tri->verts[ 3 ].Clear();
|
||||
tri->verts[ 3 ].normal.Set( 1.0f, 0.0f, 0.0f );
|
||||
tri->verts[ 3 ].tangents[0].Set( 0.0f, 1.0f, 0.0f );
|
||||
tri->verts[ 3 ].tangents[1].Set( 0.0f, 0.0f, 1.0f );
|
||||
tri->verts[ 3 ].st[ 0 ] = 0.0f;
|
||||
tri->verts[ 3 ].st[ 1 ] = 1.0f;
|
||||
|
||||
tri->indexes[ 0 ] = 0;
|
||||
tri->indexes[ 1 ] = 1;
|
||||
tri->indexes[ 2 ] = 3;
|
||||
tri->indexes[ 3 ] = 1;
|
||||
tri->indexes[ 4 ] = 2;
|
||||
tri->indexes[ 5 ] = 3;
|
||||
|
||||
tri->numVerts = 4;
|
||||
tri->numIndexes = 6;
|
||||
|
||||
surf.geometry = tri;
|
||||
surf.id = 0;
|
||||
surf.shader = tr.defaultMaterial;
|
||||
staticModel->AddSurface( surf );
|
||||
}
|
||||
|
||||
int red = idMath::FtoiFast( renderEntity->shaderParms[ SHADERPARM_RED ] * 255.0f );
|
||||
int green = idMath::FtoiFast( renderEntity->shaderParms[ SHADERPARM_GREEN ] * 255.0f );
|
||||
int blue = idMath::FtoiFast( renderEntity->shaderParms[ SHADERPARM_BLUE ] * 255.0f );
|
||||
int alpha = idMath::FtoiFast( renderEntity->shaderParms[ SHADERPARM_ALPHA ] * 255.0f );
|
||||
|
||||
idVec3 right = idVec3( 0.0f, renderEntity->shaderParms[ SHADERPARM_SPRITE_WIDTH ] * 0.5f, 0.0f );
|
||||
idVec3 up = idVec3( 0.0f, 0.0f, renderEntity->shaderParms[ SHADERPARM_SPRITE_HEIGHT ] * 0.5f );
|
||||
|
||||
tri->verts[ 0 ].xyz = up + right;
|
||||
tri->verts[ 0 ].color[ 0 ] = red;
|
||||
tri->verts[ 0 ].color[ 1 ] = green;
|
||||
tri->verts[ 0 ].color[ 2 ] = blue;
|
||||
tri->verts[ 0 ].color[ 3 ] = alpha;
|
||||
|
||||
tri->verts[ 1 ].xyz = up - right;
|
||||
tri->verts[ 1 ].color[ 0 ] = red;
|
||||
tri->verts[ 1 ].color[ 1 ] = green;
|
||||
tri->verts[ 1 ].color[ 2 ] = blue;
|
||||
tri->verts[ 1 ].color[ 3 ] = alpha;
|
||||
|
||||
tri->verts[ 2 ].xyz = - right - up;
|
||||
tri->verts[ 2 ].color[ 0 ] = red;
|
||||
tri->verts[ 2 ].color[ 1 ] = green;
|
||||
tri->verts[ 2 ].color[ 2 ] = blue;
|
||||
tri->verts[ 2 ].color[ 3 ] = alpha;
|
||||
|
||||
tri->verts[ 3 ].xyz = right - up;
|
||||
tri->verts[ 3 ].color[ 0 ] = red;
|
||||
tri->verts[ 3 ].color[ 1 ] = green;
|
||||
tri->verts[ 3 ].color[ 2 ] = blue;
|
||||
tri->verts[ 3 ].color[ 3 ] = alpha;
|
||||
|
||||
R_BoundTriSurf( tri );
|
||||
|
||||
staticModel->bounds = tri->bounds;
|
||||
|
||||
return staticModel;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
idRenderModelSprite::Bounds
|
||||
===============
|
||||
*/
|
||||
idBounds idRenderModelSprite::Bounds( const struct renderEntity_s *renderEntity ) const {
|
||||
idBounds b;
|
||||
|
||||
b.Zero();
|
||||
if ( renderEntity == NULL ) {
|
||||
b.ExpandSelf( 8.0f );
|
||||
} else {
|
||||
b.ExpandSelf( Max( renderEntity->shaderParms[ SHADERPARM_SPRITE_WIDTH ], renderEntity->shaderParms[ SHADERPARM_SPRITE_HEIGHT ] ) * 0.5f );
|
||||
}
|
||||
return b;
|
||||
}
|
||||
117
neo/renderer/RenderEntity.cpp
Normal file
117
neo/renderer/RenderEntity.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
idRenderEntityLocal::idRenderEntityLocal() {
|
||||
memset( &parms, 0, sizeof( parms ) );
|
||||
memset( modelMatrix, 0, sizeof( modelMatrix ) );
|
||||
|
||||
world = NULL;
|
||||
index = 0;
|
||||
lastModifiedFrameNum = 0;
|
||||
archived = false;
|
||||
dynamicModel = NULL;
|
||||
dynamicModelFrameCount = 0;
|
||||
cachedDynamicModel = NULL;
|
||||
referenceBounds = bounds_zero;
|
||||
viewCount = 0;
|
||||
viewEntity = NULL;
|
||||
visibleCount = 0;
|
||||
decals = NULL;
|
||||
overlay = NULL;
|
||||
entityRefs = NULL;
|
||||
firstInteraction = NULL;
|
||||
lastInteraction = NULL;
|
||||
needsPortalSky = false;
|
||||
}
|
||||
|
||||
void idRenderEntityLocal::FreeRenderEntity() {
|
||||
}
|
||||
|
||||
void idRenderEntityLocal::UpdateRenderEntity( const renderEntity_t *re, bool forceUpdate ) {
|
||||
}
|
||||
|
||||
void idRenderEntityLocal::GetRenderEntity( renderEntity_t *re ) {
|
||||
}
|
||||
|
||||
void idRenderEntityLocal::ForceUpdate() {
|
||||
}
|
||||
|
||||
int idRenderEntityLocal::GetIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
void idRenderEntityLocal::ProjectOverlay( const idPlane localTextureAxis[2], const idMaterial *material ) {
|
||||
}
|
||||
void idRenderEntityLocal::RemoveDecals() {
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
idRenderLightLocal::idRenderLightLocal() {
|
||||
memset( &parms, 0, sizeof( parms ) );
|
||||
memset( modelMatrix, 0, sizeof( modelMatrix ) );
|
||||
memset( shadowFrustums, 0, sizeof( shadowFrustums ) );
|
||||
memset( lightProject, 0, sizeof( lightProject ) );
|
||||
memset( frustum, 0, sizeof( frustum ) );
|
||||
memset( frustumWindings, 0, sizeof( frustumWindings ) );
|
||||
|
||||
lightHasMoved = false;
|
||||
world = NULL;
|
||||
index = 0;
|
||||
areaNum = 0;
|
||||
lastModifiedFrameNum = 0;
|
||||
archived = false;
|
||||
lightShader = NULL;
|
||||
falloffImage = NULL;
|
||||
globalLightOrigin = vec3_zero;
|
||||
frustumTris = NULL;
|
||||
numShadowFrustums = 0;
|
||||
viewCount = 0;
|
||||
viewLight = NULL;
|
||||
references = NULL;
|
||||
foggedPortals = NULL;
|
||||
firstInteraction = NULL;
|
||||
lastInteraction = NULL;
|
||||
}
|
||||
|
||||
void idRenderLightLocal::FreeRenderLight() {
|
||||
}
|
||||
void idRenderLightLocal::UpdateRenderLight( const renderLight_t *re, bool forceUpdate ) {
|
||||
}
|
||||
void idRenderLightLocal::GetRenderLight( renderLight_t *re ) {
|
||||
}
|
||||
void idRenderLightLocal::ForceUpdate() {
|
||||
}
|
||||
int idRenderLightLocal::GetIndex() {
|
||||
return index;
|
||||
}
|
||||
1041
neo/renderer/RenderSystem.cpp
Normal file
1041
neo/renderer/RenderSystem.cpp
Normal file
File diff suppressed because it is too large
Load Diff
276
neo/renderer/RenderSystem.h
Normal file
276
neo/renderer/RenderSystem.h
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __RENDERER_H__
|
||||
#define __RENDERER_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
idRenderSystem is responsible for managing the screen, which can have
|
||||
multiple idRenderWorld and 2D drawing done on it.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
|
||||
// Contains variables specific to the OpenGL configuration being run right now.
|
||||
// These are constant once the OpenGL subsystem is initialized.
|
||||
typedef struct glconfig_s {
|
||||
const char *renderer_string;
|
||||
const char *vendor_string;
|
||||
const char *version_string;
|
||||
const char *extensions_string;
|
||||
const char *wgl_extensions_string;
|
||||
|
||||
float glVersion; // atof( version_string )
|
||||
|
||||
|
||||
int maxTextureSize; // queried from GL
|
||||
int maxTextureUnits;
|
||||
int maxTextureCoords;
|
||||
int maxTextureImageUnits;
|
||||
float maxTextureAnisotropy;
|
||||
|
||||
int colorBits, depthBits, stencilBits;
|
||||
|
||||
bool multitextureAvailable;
|
||||
bool textureCompressionAvailable;
|
||||
bool anisotropicAvailable;
|
||||
bool textureLODBiasAvailable;
|
||||
bool textureEnvAddAvailable;
|
||||
bool textureEnvCombineAvailable;
|
||||
bool registerCombinersAvailable;
|
||||
bool cubeMapAvailable;
|
||||
bool envDot3Available;
|
||||
bool texture3DAvailable;
|
||||
bool sharedTexturePaletteAvailable;
|
||||
bool ARBVertexBufferObjectAvailable;
|
||||
bool ARBVertexProgramAvailable;
|
||||
bool ARBFragmentProgramAvailable;
|
||||
bool twoSidedStencilAvailable;
|
||||
bool textureNonPowerOfTwoAvailable;
|
||||
bool depthBoundsTestAvailable;
|
||||
|
||||
// ati r200 extensions
|
||||
bool atiFragmentShaderAvailable;
|
||||
|
||||
// ati r300
|
||||
bool atiTwoSidedStencilAvailable;
|
||||
|
||||
int vidWidth, vidHeight; // passed to R_BeginFrame
|
||||
|
||||
int displayFrequency;
|
||||
|
||||
bool isFullscreen;
|
||||
|
||||
bool allowNV30Path;
|
||||
bool allowNV20Path;
|
||||
bool allowNV10Path;
|
||||
bool allowR200Path;
|
||||
bool allowARB2Path;
|
||||
|
||||
bool isInitialized;
|
||||
} glconfig_t;
|
||||
|
||||
|
||||
// font support
|
||||
const int GLYPH_START = 0;
|
||||
const int GLYPH_END = 255;
|
||||
const int GLYPH_CHARSTART = 32;
|
||||
const int GLYPH_CHAREND = 127;
|
||||
const int GLYPHS_PER_FONT = GLYPH_END - GLYPH_START + 1;
|
||||
|
||||
typedef struct {
|
||||
int height; // number of scan lines
|
||||
int top; // top of glyph in buffer
|
||||
int bottom; // bottom of glyph in buffer
|
||||
int pitch; // width for copying
|
||||
int xSkip; // x adjustment
|
||||
int imageWidth; // width of actual image
|
||||
int imageHeight; // height of actual image
|
||||
float s; // x offset in image where glyph starts
|
||||
float t; // y offset in image where glyph starts
|
||||
float s2;
|
||||
float t2;
|
||||
const idMaterial * glyph; // shader with the glyph
|
||||
char shaderName[32];
|
||||
} glyphInfo_t;
|
||||
|
||||
typedef struct {
|
||||
glyphInfo_t glyphs [GLYPHS_PER_FONT];
|
||||
float glyphScale;
|
||||
char name[64];
|
||||
} fontInfo_t;
|
||||
|
||||
typedef struct {
|
||||
fontInfo_t fontInfoSmall;
|
||||
fontInfo_t fontInfoMedium;
|
||||
fontInfo_t fontInfoLarge;
|
||||
int maxHeight;
|
||||
int maxWidth;
|
||||
int maxHeightSmall;
|
||||
int maxWidthSmall;
|
||||
int maxHeightMedium;
|
||||
int maxWidthMedium;
|
||||
int maxHeightLarge;
|
||||
int maxWidthLarge;
|
||||
char name[64];
|
||||
} fontInfoEx_t;
|
||||
|
||||
const int SMALLCHAR_WIDTH = 8;
|
||||
const int SMALLCHAR_HEIGHT = 16;
|
||||
const int BIGCHAR_WIDTH = 16;
|
||||
const int BIGCHAR_HEIGHT = 16;
|
||||
|
||||
// all drawing is done to a 640 x 480 virtual screen size
|
||||
// and will be automatically scaled to the real resolution
|
||||
const int SCREEN_WIDTH = 640;
|
||||
const int SCREEN_HEIGHT = 480;
|
||||
|
||||
class idRenderWorld;
|
||||
|
||||
|
||||
class idRenderSystem {
|
||||
public:
|
||||
|
||||
virtual ~idRenderSystem() {}
|
||||
|
||||
// set up cvars and basic data structures, but don't
|
||||
// init OpenGL, so it can also be used for dedicated servers
|
||||
virtual void Init( void ) = 0;
|
||||
|
||||
// only called before quitting
|
||||
virtual void Shutdown( void ) = 0;
|
||||
|
||||
virtual void InitOpenGL( void ) = 0;
|
||||
|
||||
virtual void ShutdownOpenGL( void ) = 0;
|
||||
|
||||
virtual bool IsOpenGLRunning( void ) const = 0;
|
||||
|
||||
virtual bool IsFullScreen( void ) const = 0;
|
||||
virtual int GetScreenWidth( void ) const = 0;
|
||||
virtual int GetScreenHeight( void ) const = 0;
|
||||
|
||||
// allocate a renderWorld to be used for drawing
|
||||
virtual idRenderWorld * AllocRenderWorld( void ) = 0;
|
||||
virtual void FreeRenderWorld( idRenderWorld * rw ) = 0;
|
||||
|
||||
// All data that will be used in a level should be
|
||||
// registered before rendering any frames to prevent disk hits,
|
||||
// but they can still be registered at a later time
|
||||
// if necessary.
|
||||
virtual void BeginLevelLoad( void ) = 0;
|
||||
virtual void EndLevelLoad( void ) = 0;
|
||||
|
||||
// font support
|
||||
virtual bool RegisterFont( const char *fontName, fontInfoEx_t &font ) = 0;
|
||||
|
||||
// GUI drawing just involves shader parameter setting and axial image subsections
|
||||
virtual void SetColor( const idVec4 &rgba ) = 0;
|
||||
virtual void SetColor4( float r, float g, float b, float a ) = 0;
|
||||
|
||||
virtual void DrawStretchPic( const idDrawVert *verts, const glIndex_t *indexes, int vertCount, int indexCount, const idMaterial *material,
|
||||
bool clip = true, float min_x = 0.0f, float min_y = 0.0f, float max_x = 640.0f, float max_y = 480.0f ) = 0;
|
||||
virtual void DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial *material ) = 0;
|
||||
|
||||
virtual void DrawStretchTri ( idVec2 p1, idVec2 p2, idVec2 p3, idVec2 t1, idVec2 t2, idVec2 t3, const idMaterial *material ) = 0;
|
||||
virtual void GlobalToNormalizedDeviceCoordinates( const idVec3 &global, idVec3 &ndc ) = 0;
|
||||
virtual void GetGLSettings( int& width, int& height ) = 0;
|
||||
virtual void PrintMemInfo( MemInfo_t *mi ) = 0;
|
||||
|
||||
virtual void DrawSmallChar( int x, int y, int ch, const idMaterial *material ) = 0;
|
||||
virtual void DrawSmallStringExt( int x, int y, const char *string, const idVec4 &setColor, bool forceColor, const idMaterial *material ) = 0;
|
||||
virtual void DrawBigChar( int x, int y, int ch, const idMaterial *material ) = 0;
|
||||
virtual void DrawBigStringExt( int x, int y, const char *string, const idVec4 &setColor, bool forceColor, const idMaterial *material ) = 0;
|
||||
|
||||
// dump all 2D drawing so far this frame to the demo file
|
||||
virtual void WriteDemoPics() = 0;
|
||||
|
||||
// draw the 2D pics that were saved out with the current demo frame
|
||||
virtual void DrawDemoPics() = 0;
|
||||
|
||||
// FIXME: add an interface for arbitrary point/texcoord drawing
|
||||
|
||||
|
||||
// a frame cam consist of 2D drawing and potentially multiple 3D scenes
|
||||
// window sizes are needed to convert SCREEN_WIDTH / SCREEN_HEIGHT values
|
||||
virtual void BeginFrame( int windowWidth, int windowHeight ) = 0;
|
||||
|
||||
// if the pointers are not NULL, timing info will be returned
|
||||
virtual void EndFrame( int *frontEndMsec, int *backEndMsec ) = 0;
|
||||
|
||||
// aviDemo uses this.
|
||||
// Will automatically tile render large screen shots if necessary
|
||||
// Samples is the number of jittered frames for anti-aliasing
|
||||
// If ref == NULL, session->updateScreen will be used
|
||||
// This will perform swapbuffers, so it is NOT an approppriate way to
|
||||
// generate image files that happen during gameplay, as for savegame
|
||||
// markers. Use WriteRender() instead.
|
||||
virtual void TakeScreenshot( int width, int height, const char *fileName, int samples, struct renderView_s *ref ) = 0;
|
||||
|
||||
// the render output can be cropped down to a subset of the real screen, as
|
||||
// for save-game reviews and split-screen multiplayer. Users of the renderer
|
||||
// will not know the actual pixel size of the area they are rendering to
|
||||
|
||||
// the x,y,width,height values are in virtual SCREEN_WIDTH / SCREEN_HEIGHT coordinates
|
||||
|
||||
// to render to a texture, first set the crop size with makePowerOfTwo = true,
|
||||
// then perform all desired rendering, then capture to an image
|
||||
// if the specified physical dimensions are larger than the current cropped region, they will be cut down to fit
|
||||
virtual void CropRenderSize( int width, int height, bool makePowerOfTwo = false, bool forceDimensions = false ) = 0;
|
||||
virtual void CaptureRenderToImage( const char *imageName ) = 0;
|
||||
// fixAlpha will set all the alpha channel values to 0xff, which allows screen captures
|
||||
// to use the default tga loading code without having dimmed down areas in many places
|
||||
virtual void CaptureRenderToFile( const char *fileName, bool fixAlpha = false ) = 0;
|
||||
virtual void UnCrop() = 0;
|
||||
virtual void GetCardCaps( bool &oldCard, bool &nv10or20 ) = 0;
|
||||
|
||||
// the image has to be already loaded ( most straightforward way would be through a FindMaterial )
|
||||
// texture filter / mipmapping / repeat won't be modified by the upload
|
||||
// returns false if the image wasn't found
|
||||
virtual bool UploadImage( const char *imageName, const byte *data, int width, int height ) = 0;
|
||||
};
|
||||
|
||||
extern idRenderSystem * renderSystem;
|
||||
|
||||
//
|
||||
// functions mainly intended for editor and dmap integration
|
||||
//
|
||||
|
||||
// returns the frustum planes in world space
|
||||
void R_RenderLightFrustum( const struct renderLight_s &renderLight, idPlane lightFrustum[6] );
|
||||
|
||||
// for use by dmap to do the carving-on-light-boundaries and for the editor for display
|
||||
void R_LightProjectionMatrix( const idVec3 &origin, const idPlane &rearPlane, idVec4 mat[4] );
|
||||
|
||||
// used by the view shot taker
|
||||
void R_ScreenshotFilename( int &lastNumber, const char *base, idStr &fileName );
|
||||
|
||||
#endif /* !__RENDERER_H__ */
|
||||
2324
neo/renderer/RenderSystem_init.cpp
Normal file
2324
neo/renderer/RenderSystem_init.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2142
neo/renderer/RenderWorld.cpp
Normal file
2142
neo/renderer/RenderWorld.cpp
Normal file
File diff suppressed because it is too large
Load Diff
424
neo/renderer/RenderWorld.h
Normal file
424
neo/renderer/RenderWorld.h
Normal file
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __RENDERWORLD_H__
|
||||
#define __RENDERWORLD_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Render World
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
#define PROC_FILE_EXT "proc"
|
||||
#define PROC_FILE_ID "mapProcFile003"
|
||||
|
||||
// shader parms
|
||||
const int MAX_GLOBAL_SHADER_PARMS = 12;
|
||||
|
||||
const int SHADERPARM_RED = 0;
|
||||
const int SHADERPARM_GREEN = 1;
|
||||
const int SHADERPARM_BLUE = 2;
|
||||
const int SHADERPARM_ALPHA = 3;
|
||||
const int SHADERPARM_TIMESCALE = 3;
|
||||
const int SHADERPARM_TIMEOFFSET = 4;
|
||||
const int SHADERPARM_DIVERSITY = 5; // random between 0.0 and 1.0 for some effects (muzzle flashes, etc)
|
||||
const int SHADERPARM_MODE = 7; // for selecting which shader passes to enable
|
||||
const int SHADERPARM_TIME_OF_DEATH = 7; // for the monster skin-burn-away effect enable and time offset
|
||||
|
||||
// model parms
|
||||
const int SHADERPARM_MD5_SKINSCALE = 8; // for scaling vertex offsets on md5 models (jack skellington effect)
|
||||
|
||||
const int SHADERPARM_MD3_FRAME = 8;
|
||||
const int SHADERPARM_MD3_LASTFRAME = 9;
|
||||
const int SHADERPARM_MD3_BACKLERP = 10;
|
||||
|
||||
const int SHADERPARM_BEAM_END_X = 8; // for _beam models
|
||||
const int SHADERPARM_BEAM_END_Y = 9;
|
||||
const int SHADERPARM_BEAM_END_Z = 10;
|
||||
const int SHADERPARM_BEAM_WIDTH = 11;
|
||||
|
||||
const int SHADERPARM_SPRITE_WIDTH = 8;
|
||||
const int SHADERPARM_SPRITE_HEIGHT = 9;
|
||||
|
||||
const int SHADERPARM_PARTICLE_STOPTIME = 8; // don't spawn any more particles after this time
|
||||
|
||||
// guis
|
||||
const int MAX_RENDERENTITY_GUI = 3;
|
||||
|
||||
|
||||
typedef bool(*deferredEntityCallback_t)( renderEntity_s *, const renderView_s * );
|
||||
|
||||
|
||||
typedef struct renderEntity_s {
|
||||
idRenderModel * hModel; // this can only be null if callback is set
|
||||
|
||||
int entityNum;
|
||||
int bodyId;
|
||||
|
||||
// Entities that are expensive to generate, like skeletal models, can be
|
||||
// deferred until their bounds are found to be in view, in the frustum
|
||||
// of a shadowing light that is in view, or contacted by a trace / overlay test.
|
||||
// This is also used to do visual cueing on items in the view
|
||||
// The renderView may be NULL if the callback is being issued for a non-view related
|
||||
// source.
|
||||
// The callback function should clear renderEntity->callback if it doesn't
|
||||
// want to be called again next time the entity is referenced (ie, if the
|
||||
// callback has now made the entity valid until the next updateEntity)
|
||||
idBounds bounds; // only needs to be set for deferred models and md5s
|
||||
deferredEntityCallback_t callback;
|
||||
|
||||
void * callbackData; // used for whatever the callback wants
|
||||
|
||||
// player bodies and possibly player shadows should be suppressed in views from
|
||||
// that player's eyes, but will show up in mirrors and other subviews
|
||||
// security cameras could suppress their model in their subviews if we add a way
|
||||
// of specifying a view number for a remoteRenderMap view
|
||||
int suppressSurfaceInViewID;
|
||||
int suppressShadowInViewID;
|
||||
|
||||
// world models for the player and weapons will not cast shadows from view weapon
|
||||
// muzzle flashes
|
||||
int suppressShadowInLightID;
|
||||
|
||||
// if non-zero, the surface and shadow (if it casts one)
|
||||
// will only show up in the specific view, ie: player weapons
|
||||
int allowSurfaceInViewID;
|
||||
|
||||
// positioning
|
||||
// axis rotation vectors must be unit length for many
|
||||
// R_LocalToGlobal functions to work, so don't scale models!
|
||||
// axis vectors are [0] = forward, [1] = left, [2] = up
|
||||
idVec3 origin;
|
||||
idMat3 axis;
|
||||
|
||||
// texturing
|
||||
const idMaterial * customShader; // if non-0, all surfaces will use this
|
||||
const idMaterial * referenceShader; // used so flares can reference the proper light shader
|
||||
const idDeclSkin * customSkin; // 0 for no remappings
|
||||
class idSoundEmitter * referenceSound; // for shader sound tables, allowing effects to vary with sounds
|
||||
float shaderParms[ MAX_ENTITY_SHADER_PARMS ]; // can be used in any way by shader or model generation
|
||||
|
||||
// networking: see WriteGUIToSnapshot / ReadGUIFromSnapshot
|
||||
class idUserInterface * gui[ MAX_RENDERENTITY_GUI ];
|
||||
|
||||
struct renderView_s * remoteRenderView; // any remote camera surfaces will use this
|
||||
|
||||
int numJoints;
|
||||
idJointMat * joints; // array of joints that will modify vertices.
|
||||
// NULL if non-deformable model. NOT freed by renderer
|
||||
|
||||
float modelDepthHack; // squash depth range so particle effects don't clip into walls
|
||||
|
||||
// options to override surface shader flags (replace with material parameters?)
|
||||
bool noSelfShadow; // cast shadows onto other objects,but not self
|
||||
bool noShadow; // no shadow at all
|
||||
|
||||
bool noDynamicInteractions; // don't create any light / shadow interactions after
|
||||
// the level load is completed. This is a performance hack
|
||||
// for the gigantic outdoor meshes in the monorail map, so
|
||||
// all the lights in the moving monorail don't touch the meshes
|
||||
|
||||
bool weaponDepthHack; // squash depth range so view weapons don't poke into walls
|
||||
// this automatically implies noShadow
|
||||
int forceUpdate; // force an update (NOTE: not a bool to keep this struct a multiple of 4 bytes)
|
||||
int timeGroup;
|
||||
int xrayIndex;
|
||||
} renderEntity_t;
|
||||
|
||||
|
||||
typedef struct renderLight_s {
|
||||
idMat3 axis; // rotation vectors, must be unit length
|
||||
idVec3 origin;
|
||||
|
||||
// if non-zero, the light will not show up in the specific view,
|
||||
// which may be used if we want to have slightly different muzzle
|
||||
// flash lights for the player and other views
|
||||
int suppressLightInViewID;
|
||||
|
||||
// if non-zero, the light will only show up in the specific view
|
||||
// which can allow player gun gui lights and such to not effect everyone
|
||||
int allowLightInViewID;
|
||||
|
||||
// I am sticking the four bools together so there are no unused gaps in
|
||||
// the padded structure, which could confuse the memcmp that checks for redundant
|
||||
// updates
|
||||
bool noShadows; // (should we replace this with material parameters on the shader?)
|
||||
bool noSpecular; // (should we replace this with material parameters on the shader?)
|
||||
|
||||
bool pointLight; // otherwise a projection light (should probably invert the sense of this, because points are way more common)
|
||||
bool parallel; // lightCenter gives the direction to the light at infinity
|
||||
idVec3 lightRadius; // xyz radius for point lights
|
||||
idVec3 lightCenter; // offset the lighting direction for shading and
|
||||
// shadows, relative to origin
|
||||
|
||||
// frustum definition for projected lights, all reletive to origin
|
||||
// FIXME: we should probably have real plane equations here, and offer
|
||||
// a helper function for conversion from this format
|
||||
idVec3 target;
|
||||
idVec3 right;
|
||||
idVec3 up;
|
||||
idVec3 start;
|
||||
idVec3 end;
|
||||
|
||||
// Dmap will generate an optimized shadow volume named _prelight_<lightName>
|
||||
// for the light against all the _area* models in the map. The renderer will
|
||||
// ignore this value if the light has been moved after initial creation
|
||||
idRenderModel * prelightModel;
|
||||
|
||||
// muzzle flash lights will not cast shadows from player and weapon world models
|
||||
int lightId;
|
||||
|
||||
|
||||
const idMaterial * shader; // NULL = either lights/defaultPointLight or lights/defaultProjectedLight
|
||||
float shaderParms[MAX_ENTITY_SHADER_PARMS]; // can be used in any way by shader
|
||||
idSoundEmitter * referenceSound; // for shader sound tables, allowing effects to vary with sounds
|
||||
} renderLight_t;
|
||||
|
||||
|
||||
typedef struct renderView_s {
|
||||
// player views will set this to a non-zero integer for model suppress / allow
|
||||
// subviews (mirrors, cameras, etc) will always clear it to zero
|
||||
int viewID;
|
||||
|
||||
// sized from 0 to SCREEN_WIDTH / SCREEN_HEIGHT (640/480), not actual resolution
|
||||
int x, y, width, height;
|
||||
|
||||
float fov_x, fov_y;
|
||||
idVec3 vieworg;
|
||||
idMat3 viewaxis; // transformation matrix, view looks down the positive X axis
|
||||
|
||||
bool cramZNear; // for cinematics, we want to set ZNear much lower
|
||||
bool forceUpdate; // for an update
|
||||
|
||||
// time in milliseconds for shader effects and other time dependent rendering issues
|
||||
int time;
|
||||
float shaderParms[MAX_GLOBAL_SHADER_PARMS]; // can be used in any way by shader
|
||||
const idMaterial *globalMaterial; // used to override everything draw
|
||||
} renderView_t;
|
||||
|
||||
|
||||
// exitPortal_t is returned by idRenderWorld::GetPortal()
|
||||
typedef struct {
|
||||
int areas[2]; // areas connected by this portal
|
||||
const idWinding * w; // winding points have counter clockwise ordering seen from areas[0]
|
||||
int blockingBits; // PS_BLOCK_VIEW, PS_BLOCK_AIR, etc
|
||||
qhandle_t portalHandle;
|
||||
} exitPortal_t;
|
||||
|
||||
|
||||
// guiPoint_t is returned by idRenderWorld::GuiTrace()
|
||||
typedef struct {
|
||||
float x, y; // 0.0 to 1.0 range if trace hit a gui, otherwise -1
|
||||
int guiId; // id of gui ( 0, 1, or 2 ) that the trace happened against
|
||||
} guiPoint_t;
|
||||
|
||||
|
||||
// modelTrace_t is for tracing vs. visual geometry
|
||||
typedef struct modelTrace_s {
|
||||
float fraction; // fraction of trace completed
|
||||
idVec3 point; // end point of trace in global space
|
||||
idVec3 normal; // hit triangle normal vector in global space
|
||||
const idMaterial * material; // material of hit surface
|
||||
const renderEntity_t * entity; // render entity that was hit
|
||||
int jointNumber; // md5 joint nearest to the hit triangle
|
||||
} modelTrace_t;
|
||||
|
||||
|
||||
static const int NUM_PORTAL_ATTRIBUTES = 3;
|
||||
|
||||
typedef enum {
|
||||
PS_BLOCK_NONE = 0,
|
||||
|
||||
PS_BLOCK_VIEW = 1,
|
||||
PS_BLOCK_LOCATION = 2, // game map location strings often stop in hallways
|
||||
PS_BLOCK_AIR = 4, // windows between pressurized and unpresurized areas
|
||||
|
||||
PS_BLOCK_ALL = (1<<NUM_PORTAL_ATTRIBUTES)-1
|
||||
} portalConnection_t;
|
||||
|
||||
|
||||
class idRenderWorld {
|
||||
public:
|
||||
virtual ~idRenderWorld() {};
|
||||
|
||||
// The same render world can be reinitialized as often as desired
|
||||
// a NULL or empty mapName will create an empty, single area world
|
||||
virtual bool InitFromMap( const char *mapName ) = 0;
|
||||
|
||||
//-------------- Entity and Light Defs -----------------
|
||||
|
||||
// entityDefs and lightDefs are added to a given world to determine
|
||||
// what will be drawn for a rendered scene. Most update work is defered
|
||||
// until it is determined that it is actually needed for a given view.
|
||||
virtual qhandle_t AddEntityDef( const renderEntity_t *re ) = 0;
|
||||
virtual void UpdateEntityDef( qhandle_t entityHandle, const renderEntity_t *re ) = 0;
|
||||
virtual void FreeEntityDef( qhandle_t entityHandle ) = 0;
|
||||
virtual const renderEntity_t *GetRenderEntity( qhandle_t entityHandle ) const = 0;
|
||||
|
||||
virtual qhandle_t AddLightDef( const renderLight_t *rlight ) = 0;
|
||||
virtual void UpdateLightDef( qhandle_t lightHandle, const renderLight_t *rlight ) = 0;
|
||||
virtual void FreeLightDef( qhandle_t lightHandle ) = 0;
|
||||
virtual const renderLight_t *GetRenderLight( qhandle_t lightHandle ) const = 0;
|
||||
|
||||
// Force the generation of all light / surface interactions at the start of a level
|
||||
// If this isn't called, they will all be dynamically generated
|
||||
virtual void GenerateAllInteractions() = 0;
|
||||
|
||||
// returns true if this area model needs portal sky to draw
|
||||
virtual bool CheckAreaForPortalSky( int areaNum ) = 0;
|
||||
|
||||
//-------------- Decals and Overlays -----------------
|
||||
|
||||
// Creates decals on all world surfaces that the winding projects onto.
|
||||
// The projection origin should be infront of the winding plane.
|
||||
// The decals are projected onto world geometry between the winding plane and the projection origin.
|
||||
// The decals are depth faded from the winding plane to a certain distance infront of the
|
||||
// winding plane and the same distance from the projection origin towards the winding.
|
||||
virtual void ProjectDecalOntoWorld( const idFixedWinding &winding, const idVec3 &projectionOrigin, const bool parallel, const float fadeDepth, const idMaterial *material, const int startTime ) = 0;
|
||||
|
||||
// Creates decals on static models.
|
||||
virtual void ProjectDecal( qhandle_t entityHandle, const idFixedWinding &winding, const idVec3 &projectionOrigin, const bool parallel, const float fadeDepth, const idMaterial *material, const int startTime ) = 0;
|
||||
|
||||
// Creates overlays on dynamic models.
|
||||
virtual void ProjectOverlay( qhandle_t entityHandle, const idPlane localTextureAxis[2], const idMaterial *material ) = 0;
|
||||
|
||||
// Removes all decals and overlays from the given entity def.
|
||||
virtual void RemoveDecals( qhandle_t entityHandle ) = 0;
|
||||
|
||||
//-------------- Scene Rendering -----------------
|
||||
|
||||
// some calls to material functions use the current renderview time when servicing cinematics. this function
|
||||
// ensures that any parms accessed (such as time) are properly set.
|
||||
virtual void SetRenderView( const renderView_t *renderView ) = 0;
|
||||
|
||||
// rendering a scene may actually render multiple subviews for mirrors and portals, and
|
||||
// may render composite textures for gui console screens and light projections
|
||||
// It would also be acceptable to render a scene multiple times, for "rear view mirrors", etc
|
||||
virtual void RenderScene( const renderView_t *renderView ) = 0;
|
||||
|
||||
//-------------- Portal Area Information -----------------
|
||||
|
||||
// returns the number of portals
|
||||
virtual int NumPortals( void ) const = 0;
|
||||
|
||||
// returns 0 if no portal contacts the bounds
|
||||
// This is used by the game to identify portals that are contained
|
||||
// inside doors, so the connection between areas can be topologically
|
||||
// terminated when the door shuts.
|
||||
virtual qhandle_t FindPortal( const idBounds &b ) const = 0;
|
||||
|
||||
// doors explicitly close off portals when shut
|
||||
// multiple bits can be set to block multiple things, ie: ( PS_VIEW | PS_LOCATION | PS_AIR )
|
||||
virtual void SetPortalState( qhandle_t portal, int blockingBits ) = 0;
|
||||
virtual int GetPortalState( qhandle_t portal ) = 0;
|
||||
|
||||
// returns true only if a chain of portals without the given connection bits set
|
||||
// exists between the two areas (a door doesn't separate them, etc)
|
||||
virtual bool AreasAreConnected( int areaNum1, int areaNum2, portalConnection_t connection ) = 0;
|
||||
|
||||
// returns the number of portal areas in a map, so game code can build information
|
||||
// tables for the different areas
|
||||
virtual int NumAreas( void ) const = 0;
|
||||
|
||||
// Will return -1 if the point is not in an area, otherwise
|
||||
// it will return 0 <= value < NumAreas()
|
||||
virtual int PointInArea( const idVec3 &point ) const = 0;
|
||||
|
||||
// fills the *areas array with the numbers of the areas the bounds cover
|
||||
// returns the total number of areas the bounds cover
|
||||
virtual int BoundsInAreas( const idBounds &bounds, int *areas, int maxAreas ) const = 0;
|
||||
|
||||
// Used by the sound system to do area flowing
|
||||
virtual int NumPortalsInArea( int areaNum ) = 0;
|
||||
|
||||
// returns one portal from an area
|
||||
virtual exitPortal_t GetPortal( int areaNum, int portalNum ) = 0;
|
||||
|
||||
//-------------- Tracing -----------------
|
||||
|
||||
// Checks a ray trace against any gui surfaces in an entity, returning the
|
||||
// fraction location of the trace on the gui surface, or -1,-1 if no hit.
|
||||
// This doesn't do any occlusion testing, simply ignoring non-gui surfaces.
|
||||
// start / end are in global world coordinates.
|
||||
virtual guiPoint_t GuiTrace( qhandle_t entityHandle, const idVec3 start, const idVec3 end ) const = 0;
|
||||
|
||||
// Traces vs the render model, possibly instantiating a dynamic version, and returns true if something was hit
|
||||
virtual bool ModelTrace( modelTrace_t &trace, qhandle_t entityHandle, const idVec3 &start, const idVec3 &end, const float radius ) const = 0;
|
||||
|
||||
// Traces vs the whole rendered world. FIXME: we need some kind of material flags.
|
||||
virtual bool Trace( modelTrace_t &trace, const idVec3 &start, const idVec3 &end, const float radius, bool skipDynamic = true, bool skipPlayer = false ) const = 0;
|
||||
|
||||
// Traces vs the world model bsp tree.
|
||||
virtual bool FastWorldTrace( modelTrace_t &trace, const idVec3 &start, const idVec3 &end ) const = 0;
|
||||
|
||||
//-------------- Demo Control -----------------
|
||||
|
||||
// Writes a loadmap command to the demo, and clears archive counters.
|
||||
virtual void StartWritingDemo( idDemoFile *demo ) = 0;
|
||||
virtual void StopWritingDemo() = 0;
|
||||
|
||||
// Returns true when demoRenderView has been filled in.
|
||||
// adds/updates/frees entityDefs and lightDefs based on the current demo file
|
||||
// and returns the renderView to be used to render this frame.
|
||||
// a demo file may need to be advanced multiple times if the framerate
|
||||
// is less than 30hz
|
||||
// demoTimeOffset will be set if a new map load command was processed before
|
||||
// the next renderScene
|
||||
virtual bool ProcessDemoCommand( idDemoFile *readDemo, renderView_t *demoRenderView, int *demoTimeOffset ) = 0;
|
||||
|
||||
// this is used to regenerate all interactions ( which is currently only done during influences ), there may be a less
|
||||
// expensive way to do it
|
||||
virtual void RegenerateWorld() = 0;
|
||||
|
||||
//-------------- Debug Visualization -----------------
|
||||
|
||||
// Line drawing for debug visualization
|
||||
virtual void DebugClearLines( int time ) = 0; // a time of 0 will clear all lines and text
|
||||
virtual void DebugLine( const idVec4 &color, const idVec3 &start, const idVec3 &end, const int lifetime = 0, const bool depthTest = false ) = 0;
|
||||
virtual void DebugArrow( const idVec4 &color, const idVec3 &start, const idVec3 &end, int size, const int lifetime = 0 ) = 0;
|
||||
virtual void DebugWinding( const idVec4 &color, const idWinding &w, const idVec3 &origin, const idMat3 &axis, const int lifetime = 0, const bool depthTest = false ) = 0;
|
||||
virtual void DebugCircle( const idVec4 &color, const idVec3 &origin, const idVec3 &dir, const float radius, const int numSteps, const int lifetime = 0, const bool depthTest = false ) = 0;
|
||||
virtual void DebugSphere( const idVec4 &color, const idSphere &sphere, const int lifetime = 0, bool depthTest = false ) = 0;
|
||||
virtual void DebugBounds( const idVec4 &color, const idBounds &bounds, const idVec3 &org = vec3_origin, const int lifetime = 0 ) = 0;
|
||||
virtual void DebugBox( const idVec4 &color, const idBox &box, const int lifetime = 0 ) = 0;
|
||||
virtual void DebugFrustum( const idVec4 &color, const idFrustum &frustum, const bool showFromOrigin = false, const int lifetime = 0 ) = 0;
|
||||
virtual void DebugCone( const idVec4 &color, const idVec3 &apex, const idVec3 &dir, float radius1, float radius2, const int lifetime = 0 ) = 0;
|
||||
virtual void DebugAxis( const idVec3 &origin, const idMat3 &axis ) = 0;
|
||||
|
||||
// Polygon drawing for debug visualization.
|
||||
virtual void DebugClearPolygons( int time ) = 0; // a time of 0 will clear all polygons
|
||||
virtual void DebugPolygon( const idVec4 &color, const idWinding &winding, const int lifeTime = 0, const bool depthTest = false ) = 0;
|
||||
|
||||
// Text drawing for debug visualization.
|
||||
virtual void DrawText( const char *text, const idVec3 &origin, float scale, const idVec4 &color, const idMat3 &viewAxis, const int align = 1, const int lifetime = 0, bool depthTest = false ) = 0;
|
||||
};
|
||||
|
||||
#endif /* !__RENDERWORLD_H__ */
|
||||
729
neo/renderer/RenderWorld_demo.cpp
Normal file
729
neo/renderer/RenderWorld_demo.cpp
Normal file
@@ -0,0 +1,729 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
//#define WRITE_GUIS
|
||||
|
||||
typedef struct {
|
||||
int version;
|
||||
int sizeofRenderEntity;
|
||||
int sizeofRenderLight;
|
||||
char mapname[256];
|
||||
} demoHeader_t;
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
StartWritingDemo
|
||||
==============
|
||||
*/
|
||||
void idRenderWorldLocal::StartWritingDemo( idDemoFile *demo ) {
|
||||
int i;
|
||||
|
||||
// FIXME: we should track the idDemoFile locally, instead of snooping into session for it
|
||||
|
||||
WriteLoadMap();
|
||||
|
||||
// write the door portal state
|
||||
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
|
||||
if ( doublePortals[i].blockingBits ) {
|
||||
SetPortalState( i+1, doublePortals[i].blockingBits );
|
||||
}
|
||||
}
|
||||
|
||||
// clear the archive counter on all defs
|
||||
for ( i = 0 ; i < lightDefs.Num() ; i++ ) {
|
||||
if ( lightDefs[i] ) {
|
||||
lightDefs[i]->archived = false;
|
||||
}
|
||||
}
|
||||
for ( i = 0 ; i < entityDefs.Num() ; i++ ) {
|
||||
if ( entityDefs[i] ) {
|
||||
entityDefs[i]->archived = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void idRenderWorldLocal::StopWritingDemo() {
|
||||
// writeDemo = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
ProcessDemoCommand
|
||||
==============
|
||||
*/
|
||||
bool idRenderWorldLocal::ProcessDemoCommand( idDemoFile *readDemo, renderView_t *renderView, int *demoTimeOffset ) {
|
||||
bool newMap = false;
|
||||
|
||||
if ( !readDemo ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
demoCommand_t dc;
|
||||
qhandle_t h;
|
||||
|
||||
if ( !readDemo->ReadInt( (int&)dc ) ) {
|
||||
// a demoShot may not have an endFrame, but it is still valid
|
||||
return false;
|
||||
}
|
||||
|
||||
switch( dc ) {
|
||||
case DC_LOADMAP:
|
||||
// read the initial data
|
||||
demoHeader_t header;
|
||||
|
||||
readDemo->ReadInt( header.version );
|
||||
readDemo->ReadInt( header.sizeofRenderEntity );
|
||||
readDemo->ReadInt( header.sizeofRenderLight );
|
||||
for ( int i = 0; i < 256; i++ )
|
||||
readDemo->ReadChar( header.mapname[i] );
|
||||
// the internal version value got replaced by DS_VERSION at toplevel
|
||||
if ( header.version != 4 ) {
|
||||
common->Error( "Demo version mismatch.\n" );
|
||||
}
|
||||
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_LOADMAP: %s\n", header.mapname );
|
||||
}
|
||||
InitFromMap( header.mapname );
|
||||
|
||||
newMap = true; // we will need to set demoTimeOffset
|
||||
|
||||
break;
|
||||
|
||||
case DC_RENDERVIEW:
|
||||
readDemo->ReadInt( renderView->viewID );
|
||||
readDemo->ReadInt( renderView->x );
|
||||
readDemo->ReadInt( renderView->y );
|
||||
readDemo->ReadInt( renderView->width );
|
||||
readDemo->ReadInt( renderView->height );
|
||||
readDemo->ReadFloat( renderView->fov_x );
|
||||
readDemo->ReadFloat( renderView->fov_y );
|
||||
readDemo->ReadVec3( renderView->vieworg );
|
||||
readDemo->ReadMat3( renderView->viewaxis );
|
||||
readDemo->ReadBool( renderView->cramZNear );
|
||||
readDemo->ReadBool( renderView->forceUpdate );
|
||||
// binary compatibility with win32 padded structures
|
||||
char tmp;
|
||||
readDemo->ReadChar( tmp );
|
||||
readDemo->ReadChar( tmp );
|
||||
readDemo->ReadInt( renderView->time );
|
||||
for ( int i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ )
|
||||
readDemo->ReadFloat( renderView->shaderParms[i] );
|
||||
|
||||
if ( !readDemo->ReadInt( (int&)renderView->globalMaterial ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_RENDERVIEW: %i\n", renderView->time );
|
||||
}
|
||||
|
||||
// possibly change the time offset if this is from a new map
|
||||
if ( newMap && demoTimeOffset ) {
|
||||
*demoTimeOffset = renderView->time - eventLoop->Milliseconds();
|
||||
}
|
||||
return false;
|
||||
|
||||
case DC_UPDATE_ENTITYDEF:
|
||||
ReadRenderEntity();
|
||||
break;
|
||||
case DC_DELETE_ENTITYDEF:
|
||||
if ( !readDemo->ReadInt( h ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_DELETE_ENTITYDEF: %i\n", h );
|
||||
}
|
||||
FreeEntityDef( h );
|
||||
break;
|
||||
case DC_UPDATE_LIGHTDEF:
|
||||
ReadRenderLight();
|
||||
break;
|
||||
case DC_DELETE_LIGHTDEF:
|
||||
if ( !readDemo->ReadInt( h ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_DELETE_LIGHTDEF: %i\n", h );
|
||||
}
|
||||
FreeLightDef( h );
|
||||
break;
|
||||
|
||||
case DC_CAPTURE_RENDER:
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_CAPTURE_RENDER\n" );
|
||||
}
|
||||
renderSystem->CaptureRenderToImage( readDemo->ReadHashString() );
|
||||
break;
|
||||
|
||||
case DC_CROP_RENDER:
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_CROP_RENDER\n" );
|
||||
}
|
||||
int size[3];
|
||||
readDemo->ReadInt( size[0] );
|
||||
readDemo->ReadInt( size[1] );
|
||||
readDemo->ReadInt( size[2] );
|
||||
renderSystem->CropRenderSize( size[0], size[1], size[2] != 0 );
|
||||
break;
|
||||
|
||||
case DC_UNCROP_RENDER:
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_UNCROP\n" );
|
||||
}
|
||||
renderSystem->UnCrop();
|
||||
break;
|
||||
|
||||
case DC_GUI_MODEL:
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_GUI_MODEL\n" );
|
||||
}
|
||||
tr.demoGuiModel->ReadFromDemo( readDemo );
|
||||
break;
|
||||
|
||||
case DC_DEFINE_MODEL:
|
||||
{
|
||||
idRenderModel *model = renderModelManager->AllocModel();
|
||||
model->ReadFromDemoFile( session->readDemo );
|
||||
// add to model manager, so we can find it
|
||||
renderModelManager->AddModel( model );
|
||||
|
||||
// save it in the list to free when clearing this map
|
||||
localModels.Append( model );
|
||||
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_DEFINE_MODEL\n" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DC_SET_PORTAL_STATE:
|
||||
{
|
||||
int data[2];
|
||||
readDemo->ReadInt( data[0] );
|
||||
readDemo->ReadInt( data[1] );
|
||||
SetPortalState( data[0], data[1] );
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_SET_PORTAL_STATE: %i %i\n", data[0], data[1] );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case DC_END_FRAME:
|
||||
return true;
|
||||
|
||||
default:
|
||||
common->Error( "Bad token in demo stream" );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
WriteLoadMap
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::WriteLoadMap() {
|
||||
|
||||
// only the main renderWorld writes stuff to demos, not the wipes or
|
||||
// menu renders
|
||||
if ( this != session->rw ) {
|
||||
return;
|
||||
}
|
||||
|
||||
session->writeDemo->WriteInt( DS_RENDER );
|
||||
session->writeDemo->WriteInt( DC_LOADMAP );
|
||||
|
||||
demoHeader_t header;
|
||||
strncpy( header.mapname, mapName.c_str(), sizeof( header.mapname ) - 1 );
|
||||
header.version = 4;
|
||||
header.sizeofRenderEntity = sizeof( renderEntity_t );
|
||||
header.sizeofRenderLight = sizeof( renderLight_t );
|
||||
session->writeDemo->WriteInt( header.version );
|
||||
session->writeDemo->WriteInt( header.sizeofRenderEntity );
|
||||
session->writeDemo->WriteInt( header.sizeofRenderLight );
|
||||
for ( int i = 0; i < 256; i++ )
|
||||
session->writeDemo->WriteChar( header.mapname[i] );
|
||||
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "write DC_DELETE_LIGHTDEF: %s\n", mapName.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
WriteVisibleDefs
|
||||
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::WriteVisibleDefs( const viewDef_t *viewDef ) {
|
||||
// only the main renderWorld writes stuff to demos, not the wipes or
|
||||
// menu renders
|
||||
if ( this != session->rw ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure all necessary entities and lights are updated
|
||||
for ( viewEntity_t *viewEnt = viewDef->viewEntitys ; viewEnt ; viewEnt = viewEnt->next ) {
|
||||
idRenderEntityLocal *ent = viewEnt->entityDef;
|
||||
|
||||
if ( ent->archived ) {
|
||||
// still up to date
|
||||
continue;
|
||||
}
|
||||
|
||||
// write it out
|
||||
WriteRenderEntity( ent->index, &ent->parms );
|
||||
ent->archived = true;
|
||||
}
|
||||
|
||||
for ( viewLight_t *viewLight = viewDef->viewLights ; viewLight ; viewLight = viewLight->next ) {
|
||||
idRenderLightLocal *light = viewLight->lightDef;
|
||||
|
||||
if ( light->archived ) {
|
||||
// still up to date
|
||||
continue;
|
||||
}
|
||||
// write it out
|
||||
WriteRenderLight( light->index, &light->parms );
|
||||
light->archived = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
WriteRenderView
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::WriteRenderView( const renderView_t *renderView ) {
|
||||
int i;
|
||||
|
||||
// only the main renderWorld writes stuff to demos, not the wipes or
|
||||
// menu renders
|
||||
if ( this != session->rw ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// write the actual view command
|
||||
session->writeDemo->WriteInt( DS_RENDER );
|
||||
session->writeDemo->WriteInt( DC_RENDERVIEW );
|
||||
session->writeDemo->WriteInt( renderView->viewID );
|
||||
session->writeDemo->WriteInt( renderView->x );
|
||||
session->writeDemo->WriteInt( renderView->y );
|
||||
session->writeDemo->WriteInt( renderView->width );
|
||||
session->writeDemo->WriteInt( renderView->height );
|
||||
session->writeDemo->WriteFloat( renderView->fov_x );
|
||||
session->writeDemo->WriteFloat( renderView->fov_y );
|
||||
session->writeDemo->WriteVec3( renderView->vieworg );
|
||||
session->writeDemo->WriteMat3( renderView->viewaxis );
|
||||
session->writeDemo->WriteBool( renderView->cramZNear );
|
||||
session->writeDemo->WriteBool( renderView->forceUpdate );
|
||||
// binary compatibility with old win32 version writing padded structures directly to disk
|
||||
session->writeDemo->WriteUnsignedChar( 0 );
|
||||
session->writeDemo->WriteUnsignedChar( 0 );
|
||||
session->writeDemo->WriteInt( renderView->time );
|
||||
for ( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ )
|
||||
session->writeDemo->WriteFloat( renderView->shaderParms[i] );
|
||||
session->writeDemo->WriteInt( (int&)renderView->globalMaterial );
|
||||
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "write DC_RENDERVIEW: %i\n", renderView->time );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
WriteFreeEntity
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::WriteFreeEntity( qhandle_t handle ) {
|
||||
|
||||
// only the main renderWorld writes stuff to demos, not the wipes or
|
||||
// menu renders
|
||||
if ( this != session->rw ) {
|
||||
return;
|
||||
}
|
||||
|
||||
session->writeDemo->WriteInt( DS_RENDER );
|
||||
session->writeDemo->WriteInt( DC_DELETE_ENTITYDEF );
|
||||
session->writeDemo->WriteInt( handle );
|
||||
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "write DC_DELETE_ENTITYDEF: %i\n", handle );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
WriteFreeLightEntity
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::WriteFreeLight( qhandle_t handle ) {
|
||||
|
||||
// only the main renderWorld writes stuff to demos, not the wipes or
|
||||
// menu renders
|
||||
if ( this != session->rw ) {
|
||||
return;
|
||||
}
|
||||
|
||||
session->writeDemo->WriteInt( DS_RENDER );
|
||||
session->writeDemo->WriteInt( DC_DELETE_LIGHTDEF );
|
||||
session->writeDemo->WriteInt( handle );
|
||||
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "write DC_DELETE_LIGHTDEF: %i\n", handle );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
WriteRenderLight
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::WriteRenderLight( qhandle_t handle, const renderLight_t *light ) {
|
||||
|
||||
// only the main renderWorld writes stuff to demos, not the wipes or
|
||||
// menu renders
|
||||
if ( this != session->rw ) {
|
||||
return;
|
||||
}
|
||||
|
||||
session->writeDemo->WriteInt( DS_RENDER );
|
||||
session->writeDemo->WriteInt( DC_UPDATE_LIGHTDEF );
|
||||
session->writeDemo->WriteInt( handle );
|
||||
|
||||
session->writeDemo->WriteMat3( light->axis );
|
||||
session->writeDemo->WriteVec3( light->origin );
|
||||
session->writeDemo->WriteInt( light->suppressLightInViewID );
|
||||
session->writeDemo->WriteInt( light->allowLightInViewID );
|
||||
session->writeDemo->WriteBool( light->noShadows );
|
||||
session->writeDemo->WriteBool( light->noSpecular );
|
||||
session->writeDemo->WriteBool( light->pointLight );
|
||||
session->writeDemo->WriteBool( light->parallel );
|
||||
session->writeDemo->WriteVec3( light->lightRadius );
|
||||
session->writeDemo->WriteVec3( light->lightCenter );
|
||||
session->writeDemo->WriteVec3( light->target );
|
||||
session->writeDemo->WriteVec3( light->right );
|
||||
session->writeDemo->WriteVec3( light->up );
|
||||
session->writeDemo->WriteVec3( light->start );
|
||||
session->writeDemo->WriteVec3( light->end );
|
||||
session->writeDemo->WriteInt( (int&)light->prelightModel );
|
||||
session->writeDemo->WriteInt( light->lightId );
|
||||
session->writeDemo->WriteInt( (int&)light->shader );
|
||||
for ( int i = 0; i < MAX_ENTITY_SHADER_PARMS; i++)
|
||||
session->writeDemo->WriteFloat( light->shaderParms[i] );
|
||||
session->writeDemo->WriteInt( (int&)light->referenceSound );
|
||||
|
||||
if ( light->prelightModel ) {
|
||||
session->writeDemo->WriteHashString( light->prelightModel->Name() );
|
||||
}
|
||||
if ( light->shader ) {
|
||||
session->writeDemo->WriteHashString( light->shader->GetName() );
|
||||
}
|
||||
if ( light->referenceSound ) {
|
||||
int index = light->referenceSound->Index();
|
||||
session->writeDemo->WriteInt( index );
|
||||
}
|
||||
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "write DC_UPDATE_LIGHTDEF: %i\n", handle );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
ReadRenderLight
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::ReadRenderLight( ) {
|
||||
renderLight_t light;
|
||||
int index;
|
||||
|
||||
session->readDemo->ReadInt( index );
|
||||
if ( index < 0 ) {
|
||||
common->Error( "ReadRenderLight: index < 0 " );
|
||||
}
|
||||
|
||||
session->readDemo->ReadMat3( light.axis );
|
||||
session->readDemo->ReadVec3( light.origin );
|
||||
session->readDemo->ReadInt( light.suppressLightInViewID );
|
||||
session->readDemo->ReadInt( light.allowLightInViewID );
|
||||
session->readDemo->ReadBool( light.noShadows );
|
||||
session->readDemo->ReadBool( light.noSpecular );
|
||||
session->readDemo->ReadBool( light.pointLight );
|
||||
session->readDemo->ReadBool( light.parallel );
|
||||
session->readDemo->ReadVec3( light.lightRadius );
|
||||
session->readDemo->ReadVec3( light.lightCenter );
|
||||
session->readDemo->ReadVec3( light.target );
|
||||
session->readDemo->ReadVec3( light.right );
|
||||
session->readDemo->ReadVec3( light.up );
|
||||
session->readDemo->ReadVec3( light.start );
|
||||
session->readDemo->ReadVec3( light.end );
|
||||
session->readDemo->ReadInt( (int&)light.prelightModel );
|
||||
session->readDemo->ReadInt( light.lightId );
|
||||
session->readDemo->ReadInt( (int&)light.shader );
|
||||
for ( int i = 0; i < MAX_ENTITY_SHADER_PARMS; i++)
|
||||
session->readDemo->ReadFloat( light.shaderParms[i] );
|
||||
session->readDemo->ReadInt( (int&)light.referenceSound );
|
||||
if ( light.prelightModel ) {
|
||||
light.prelightModel = renderModelManager->FindModel( session->readDemo->ReadHashString() );
|
||||
}
|
||||
if ( light.shader ) {
|
||||
light.shader = declManager->FindMaterial( session->readDemo->ReadHashString() );
|
||||
}
|
||||
if ( light.referenceSound ) {
|
||||
int index;
|
||||
session->readDemo->ReadInt( index );
|
||||
light.referenceSound = session->sw->EmitterForIndex( index );
|
||||
}
|
||||
|
||||
UpdateLightDef( index, &light );
|
||||
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_UPDATE_LIGHTDEF: %i\n", index );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
WriteRenderEntity
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::WriteRenderEntity( qhandle_t handle, const renderEntity_t *ent ) {
|
||||
|
||||
// only the main renderWorld writes stuff to demos, not the wipes or
|
||||
// menu renders
|
||||
if ( this != session->rw ) {
|
||||
return;
|
||||
}
|
||||
|
||||
session->writeDemo->WriteInt( DS_RENDER );
|
||||
session->writeDemo->WriteInt( DC_UPDATE_ENTITYDEF );
|
||||
session->writeDemo->WriteInt( handle );
|
||||
|
||||
session->writeDemo->WriteInt( (int&)ent->hModel );
|
||||
session->writeDemo->WriteInt( ent->entityNum );
|
||||
session->writeDemo->WriteInt( ent->bodyId );
|
||||
session->writeDemo->WriteVec3( ent->bounds[0] );
|
||||
session->writeDemo->WriteVec3( ent->bounds[1] );
|
||||
session->writeDemo->WriteInt( (int&)ent->callback );
|
||||
session->writeDemo->WriteInt( (int&)ent->callbackData );
|
||||
session->writeDemo->WriteInt( ent->suppressSurfaceInViewID );
|
||||
session->writeDemo->WriteInt( ent->suppressShadowInViewID );
|
||||
session->writeDemo->WriteInt( ent->suppressShadowInLightID );
|
||||
session->writeDemo->WriteInt( ent->allowSurfaceInViewID );
|
||||
session->writeDemo->WriteVec3( ent->origin );
|
||||
session->writeDemo->WriteMat3( ent->axis );
|
||||
session->writeDemo->WriteInt( (int&)ent->customShader );
|
||||
session->writeDemo->WriteInt( (int&)ent->referenceShader );
|
||||
session->writeDemo->WriteInt( (int&)ent->customSkin );
|
||||
session->writeDemo->WriteInt( (int&)ent->referenceSound );
|
||||
for ( int i = 0; i < MAX_ENTITY_SHADER_PARMS; i++ )
|
||||
session->writeDemo->WriteFloat( ent->shaderParms[i] );
|
||||
for ( int i = 0; i < MAX_RENDERENTITY_GUI; i++ )
|
||||
session->writeDemo->WriteInt( (int&)ent->gui[i] );
|
||||
session->writeDemo->WriteInt( (int&)ent->remoteRenderView );
|
||||
session->writeDemo->WriteInt( ent->numJoints );
|
||||
session->writeDemo->WriteInt( (int&)ent->joints );
|
||||
session->writeDemo->WriteFloat( ent->modelDepthHack );
|
||||
session->writeDemo->WriteBool( ent->noSelfShadow );
|
||||
session->writeDemo->WriteBool( ent->noShadow );
|
||||
session->writeDemo->WriteBool( ent->noDynamicInteractions );
|
||||
session->writeDemo->WriteBool( ent->weaponDepthHack );
|
||||
session->writeDemo->WriteInt( ent->forceUpdate );
|
||||
|
||||
if ( ent->customShader ) {
|
||||
session->writeDemo->WriteHashString( ent->customShader->GetName() );
|
||||
}
|
||||
if ( ent->customSkin ) {
|
||||
session->writeDemo->WriteHashString( ent->customSkin->GetName() );
|
||||
}
|
||||
if ( ent->hModel ) {
|
||||
session->writeDemo->WriteHashString( ent->hModel->Name() );
|
||||
}
|
||||
if ( ent->referenceShader ) {
|
||||
session->writeDemo->WriteHashString( ent->referenceShader->GetName() );
|
||||
}
|
||||
if ( ent->referenceSound ) {
|
||||
int index = ent->referenceSound->Index();
|
||||
session->writeDemo->WriteInt( index );
|
||||
}
|
||||
if ( ent->numJoints ) {
|
||||
for ( int i = 0; i < ent->numJoints; i++) {
|
||||
float *data = ent->joints[i].ToFloatPtr();
|
||||
for ( int j = 0; j < 12; ++j)
|
||||
session->writeDemo->WriteFloat( data[j] );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if ( ent->decals ) {
|
||||
ent->decals->WriteToDemoFile( session->readDemo );
|
||||
}
|
||||
if ( ent->overlay ) {
|
||||
ent->overlay->WriteToDemoFile( session->writeDemo );
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef WRITE_GUIS
|
||||
if ( ent->gui ) {
|
||||
ent->gui->WriteToDemoFile( session->writeDemo );
|
||||
}
|
||||
if ( ent->gui2 ) {
|
||||
ent->gui2->WriteToDemoFile( session->writeDemo );
|
||||
}
|
||||
if ( ent->gui3 ) {
|
||||
ent->gui3->WriteToDemoFile( session->writeDemo );
|
||||
}
|
||||
#endif
|
||||
|
||||
// RENDERDEMO_VERSION >= 2 ( Doom3 1.2 )
|
||||
session->writeDemo->WriteInt( ent->timeGroup );
|
||||
session->writeDemo->WriteInt( ent->xrayIndex );
|
||||
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "write DC_UPDATE_ENTITYDEF: %i = %s\n", handle, ent->hModel ? ent->hModel->Name() : "NULL" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
ReadRenderEntity
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::ReadRenderEntity() {
|
||||
renderEntity_t ent;
|
||||
int index, i;
|
||||
|
||||
session->readDemo->ReadInt( index );
|
||||
if ( index < 0 ) {
|
||||
common->Error( "ReadRenderEntity: index < 0" );
|
||||
}
|
||||
|
||||
session->readDemo->ReadInt( (int&)ent.hModel );
|
||||
session->readDemo->ReadInt( ent.entityNum );
|
||||
session->readDemo->ReadInt( ent.bodyId );
|
||||
session->readDemo->ReadVec3( ent.bounds[0] );
|
||||
session->readDemo->ReadVec3( ent.bounds[1] );
|
||||
session->readDemo->ReadInt( (int&)ent.callback );
|
||||
session->readDemo->ReadInt( (int&)ent.callbackData );
|
||||
session->readDemo->ReadInt( ent.suppressSurfaceInViewID );
|
||||
session->readDemo->ReadInt( ent.suppressShadowInViewID );
|
||||
session->readDemo->ReadInt( ent.suppressShadowInLightID );
|
||||
session->readDemo->ReadInt( ent.allowSurfaceInViewID );
|
||||
session->readDemo->ReadVec3( ent.origin );
|
||||
session->readDemo->ReadMat3( ent.axis );
|
||||
session->readDemo->ReadInt( (int&)ent.customShader );
|
||||
session->readDemo->ReadInt( (int&)ent.referenceShader );
|
||||
session->readDemo->ReadInt( (int&)ent.customSkin );
|
||||
session->readDemo->ReadInt( (int&)ent.referenceSound );
|
||||
for ( i = 0; i < MAX_ENTITY_SHADER_PARMS; i++ ) {
|
||||
session->readDemo->ReadFloat( ent.shaderParms[i] );
|
||||
}
|
||||
for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
|
||||
session->readDemo->ReadInt( (int&)ent.gui[i] );
|
||||
}
|
||||
session->readDemo->ReadInt( (int&)ent.remoteRenderView );
|
||||
session->readDemo->ReadInt( ent.numJoints );
|
||||
session->readDemo->ReadInt( (int&)ent.joints );
|
||||
session->readDemo->ReadFloat( ent.modelDepthHack );
|
||||
session->readDemo->ReadBool( ent.noSelfShadow );
|
||||
session->readDemo->ReadBool( ent.noShadow );
|
||||
session->readDemo->ReadBool( ent.noDynamicInteractions );
|
||||
session->readDemo->ReadBool( ent.weaponDepthHack );
|
||||
session->readDemo->ReadInt( ent.forceUpdate );
|
||||
ent.callback = NULL;
|
||||
if ( ent.customShader ) {
|
||||
ent.customShader = declManager->FindMaterial( session->readDemo->ReadHashString() );
|
||||
}
|
||||
if ( ent.customSkin ) {
|
||||
ent.customSkin = declManager->FindSkin( session->readDemo->ReadHashString() );
|
||||
}
|
||||
if ( ent.hModel ) {
|
||||
ent.hModel = renderModelManager->FindModel( session->readDemo->ReadHashString() );
|
||||
}
|
||||
if ( ent.referenceShader ) {
|
||||
ent.referenceShader = declManager->FindMaterial( session->readDemo->ReadHashString() );
|
||||
}
|
||||
if ( ent.referenceSound ) {
|
||||
int index;
|
||||
session->readDemo->ReadInt( index );
|
||||
ent.referenceSound = session->sw->EmitterForIndex( index );
|
||||
}
|
||||
if ( ent.numJoints ) {
|
||||
ent.joints = (idJointMat *)Mem_Alloc16( ent.numJoints * sizeof( ent.joints[0] ) );
|
||||
for ( int i = 0; i < ent.numJoints; i++) {
|
||||
float *data = ent.joints[i].ToFloatPtr();
|
||||
for ( int j = 0; j < 12; ++j)
|
||||
session->readDemo->ReadFloat( data[j] );
|
||||
}
|
||||
}
|
||||
|
||||
ent.callbackData = NULL;
|
||||
|
||||
/*
|
||||
if ( ent.decals ) {
|
||||
ent.decals = idRenderModelDecal::Alloc();
|
||||
ent.decals->ReadFromDemoFile( session->readDemo );
|
||||
}
|
||||
if ( ent.overlay ) {
|
||||
ent.overlay = idRenderModelOverlay::Alloc();
|
||||
ent.overlay->ReadFromDemoFile( session->readDemo );
|
||||
}
|
||||
*/
|
||||
|
||||
for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
|
||||
if ( ent.gui[ i ] ) {
|
||||
ent.gui[ i ] = uiManager->Alloc();
|
||||
#ifdef WRITE_GUIS
|
||||
ent.gui[ i ]->ReadFromDemoFile( session->readDemo );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// >= Doom3 v1.2 only
|
||||
if ( session->renderdemoVersion >= 2 ) {
|
||||
session->readDemo->ReadInt( ent.timeGroup );
|
||||
session->readDemo->ReadInt( ent.xrayIndex );
|
||||
} else {
|
||||
ent.timeGroup = 0;
|
||||
ent.xrayIndex = 0;
|
||||
}
|
||||
|
||||
UpdateEntityDef( index, &ent );
|
||||
|
||||
if ( r_showDemo.GetBool() ) {
|
||||
common->Printf( "DC_UPDATE_ENTITYDEF: %i = %s\n", index, ent.hModel ? ent.hModel->Name() : "NULL" );
|
||||
}
|
||||
}
|
||||
700
neo/renderer/RenderWorld_load.cpp
Normal file
700
neo/renderer/RenderWorld_load.cpp
Normal file
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
idRenderWorldLocal::FreeWorld
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::FreeWorld() {
|
||||
int i;
|
||||
|
||||
// this will free all the lightDefs and entityDefs
|
||||
FreeDefs();
|
||||
|
||||
// free all the portals and check light/model references
|
||||
for ( i = 0 ; i < numPortalAreas ; i++ ) {
|
||||
portalArea_t *area;
|
||||
portal_t *portal, *nextPortal;
|
||||
|
||||
area = &portalAreas[i];
|
||||
for ( portal = area->portals ; portal ; portal = nextPortal ) {
|
||||
nextPortal = portal->next;
|
||||
delete portal->w;
|
||||
R_StaticFree( portal );
|
||||
}
|
||||
|
||||
// there shouldn't be any remaining lightRefs or entityRefs
|
||||
if ( area->lightRefs.areaNext != &area->lightRefs ) {
|
||||
common->Error( "FreeWorld: unexpected remaining lightRefs" );
|
||||
}
|
||||
if ( area->entityRefs.areaNext != &area->entityRefs ) {
|
||||
common->Error( "FreeWorld: unexpected remaining entityRefs" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( portalAreas ) {
|
||||
R_StaticFree( portalAreas );
|
||||
portalAreas = NULL;
|
||||
numPortalAreas = 0;
|
||||
R_StaticFree( areaScreenRect );
|
||||
areaScreenRect = NULL;
|
||||
}
|
||||
|
||||
if ( doublePortals ) {
|
||||
R_StaticFree( doublePortals );
|
||||
doublePortals = NULL;
|
||||
numInterAreaPortals = 0;
|
||||
}
|
||||
|
||||
if ( areaNodes ) {
|
||||
R_StaticFree( areaNodes );
|
||||
areaNodes = NULL;
|
||||
}
|
||||
|
||||
// free all the inline idRenderModels
|
||||
for ( i = 0 ; i < localModels.Num() ; i++ ) {
|
||||
renderModelManager->RemoveModel( localModels[i] );
|
||||
delete localModels[i];
|
||||
}
|
||||
localModels.Clear();
|
||||
|
||||
areaReferenceAllocator.Shutdown();
|
||||
interactionAllocator.Shutdown();
|
||||
areaNumRefAllocator.Shutdown();
|
||||
|
||||
mapName = "<FREED>";
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idRenderWorldLocal::TouchWorldModels
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::TouchWorldModels( void ) {
|
||||
int i;
|
||||
|
||||
for ( i = 0 ; i < localModels.Num() ; i++ ) {
|
||||
renderModelManager->CheckModel( localModels[i]->Name() );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idRenderWorldLocal::ParseModel
|
||||
================
|
||||
*/
|
||||
idRenderModel *idRenderWorldLocal::ParseModel( idLexer *src ) {
|
||||
idRenderModel *model;
|
||||
idToken token;
|
||||
int i, j;
|
||||
srfTriangles_t *tri;
|
||||
modelSurface_t surf;
|
||||
|
||||
src->ExpectTokenString( "{" );
|
||||
|
||||
// parse the name
|
||||
src->ExpectAnyToken( &token );
|
||||
|
||||
model = renderModelManager->AllocModel();
|
||||
model->InitEmpty( token );
|
||||
|
||||
int numSurfaces = src->ParseInt();
|
||||
if ( numSurfaces < 0 ) {
|
||||
src->Error( "R_ParseModel: bad numSurfaces" );
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < numSurfaces ; i++ ) {
|
||||
src->ExpectTokenString( "{" );
|
||||
|
||||
src->ExpectAnyToken( &token );
|
||||
|
||||
surf.shader = declManager->FindMaterial( token );
|
||||
|
||||
((idMaterial*)surf.shader)->AddReference();
|
||||
|
||||
tri = R_AllocStaticTriSurf();
|
||||
surf.geometry = tri;
|
||||
|
||||
tri->numVerts = src->ParseInt();
|
||||
tri->numIndexes = src->ParseInt();
|
||||
|
||||
R_AllocStaticTriSurfVerts( tri, tri->numVerts );
|
||||
for ( j = 0 ; j < tri->numVerts ; j++ ) {
|
||||
float vec[8];
|
||||
|
||||
src->Parse1DMatrix( 8, vec );
|
||||
|
||||
tri->verts[j].xyz[0] = vec[0];
|
||||
tri->verts[j].xyz[1] = vec[1];
|
||||
tri->verts[j].xyz[2] = vec[2];
|
||||
tri->verts[j].st[0] = vec[3];
|
||||
tri->verts[j].st[1] = vec[4];
|
||||
tri->verts[j].normal[0] = vec[5];
|
||||
tri->verts[j].normal[1] = vec[6];
|
||||
tri->verts[j].normal[2] = vec[7];
|
||||
}
|
||||
|
||||
R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
|
||||
for ( j = 0 ; j < tri->numIndexes ; j++ ) {
|
||||
tri->indexes[j] = src->ParseInt();
|
||||
}
|
||||
src->ExpectTokenString( "}" );
|
||||
|
||||
// add the completed surface to the model
|
||||
model->AddSurface( surf );
|
||||
}
|
||||
|
||||
src->ExpectTokenString( "}" );
|
||||
|
||||
model->FinishSurfaces();
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idRenderWorldLocal::ParseShadowModel
|
||||
================
|
||||
*/
|
||||
idRenderModel *idRenderWorldLocal::ParseShadowModel( idLexer *src ) {
|
||||
idRenderModel *model;
|
||||
idToken token;
|
||||
int j;
|
||||
srfTriangles_t *tri;
|
||||
modelSurface_t surf;
|
||||
|
||||
src->ExpectTokenString( "{" );
|
||||
|
||||
// parse the name
|
||||
src->ExpectAnyToken( &token );
|
||||
|
||||
model = renderModelManager->AllocModel();
|
||||
model->InitEmpty( token );
|
||||
|
||||
surf.shader = tr.defaultMaterial;
|
||||
|
||||
tri = R_AllocStaticTriSurf();
|
||||
surf.geometry = tri;
|
||||
|
||||
tri->numVerts = src->ParseInt();
|
||||
tri->numShadowIndexesNoCaps = src->ParseInt();
|
||||
tri->numShadowIndexesNoFrontCaps = src->ParseInt();
|
||||
tri->numIndexes = src->ParseInt();
|
||||
tri->shadowCapPlaneBits = src->ParseInt();
|
||||
|
||||
R_AllocStaticTriSurfShadowVerts( tri, tri->numVerts );
|
||||
tri->bounds.Clear();
|
||||
for ( j = 0 ; j < tri->numVerts ; j++ ) {
|
||||
float vec[8];
|
||||
|
||||
src->Parse1DMatrix( 3, vec );
|
||||
tri->shadowVertexes[j].xyz[0] = vec[0];
|
||||
tri->shadowVertexes[j].xyz[1] = vec[1];
|
||||
tri->shadowVertexes[j].xyz[2] = vec[2];
|
||||
tri->shadowVertexes[j].xyz[3] = 1; // no homogenous value
|
||||
|
||||
tri->bounds.AddPoint( tri->shadowVertexes[j].xyz.ToVec3() );
|
||||
}
|
||||
|
||||
R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
|
||||
for ( j = 0 ; j < tri->numIndexes ; j++ ) {
|
||||
tri->indexes[j] = src->ParseInt();
|
||||
}
|
||||
|
||||
// add the completed surface to the model
|
||||
model->AddSurface( surf );
|
||||
|
||||
src->ExpectTokenString( "}" );
|
||||
|
||||
// we do NOT do a model->FinishSurfaceces, because we don't need sil edges, planes, tangents, etc.
|
||||
// model->FinishSurfaces();
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idRenderWorldLocal::SetupAreaRefs
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::SetupAreaRefs() {
|
||||
int i;
|
||||
|
||||
connectedAreaNum = 0;
|
||||
for ( i = 0 ; i < numPortalAreas ; i++ ) {
|
||||
portalAreas[i].areaNum = i;
|
||||
portalAreas[i].lightRefs.areaNext =
|
||||
portalAreas[i].lightRefs.areaPrev =
|
||||
&portalAreas[i].lightRefs;
|
||||
portalAreas[i].entityRefs.areaNext =
|
||||
portalAreas[i].entityRefs.areaPrev =
|
||||
&portalAreas[i].entityRefs;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idRenderWorldLocal::ParseInterAreaPortals
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::ParseInterAreaPortals( idLexer *src ) {
|
||||
int i, j;
|
||||
|
||||
src->ExpectTokenString( "{" );
|
||||
|
||||
numPortalAreas = src->ParseInt();
|
||||
if ( numPortalAreas < 0 ) {
|
||||
src->Error( "R_ParseInterAreaPortals: bad numPortalAreas" );
|
||||
return;
|
||||
}
|
||||
portalAreas = (portalArea_t *)R_ClearedStaticAlloc( numPortalAreas * sizeof( portalAreas[0] ) );
|
||||
areaScreenRect = (idScreenRect *) R_ClearedStaticAlloc( numPortalAreas * sizeof( idScreenRect ) );
|
||||
|
||||
// set the doubly linked lists
|
||||
SetupAreaRefs();
|
||||
|
||||
numInterAreaPortals = src->ParseInt();
|
||||
if ( numInterAreaPortals < 0 ) {
|
||||
src->Error( "R_ParseInterAreaPortals: bad numInterAreaPortals" );
|
||||
return;
|
||||
}
|
||||
|
||||
doublePortals = (doublePortal_t *)R_ClearedStaticAlloc( numInterAreaPortals *
|
||||
sizeof( doublePortals [0] ) );
|
||||
|
||||
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
|
||||
int numPoints, a1, a2;
|
||||
idWinding *w;
|
||||
portal_t *p;
|
||||
|
||||
numPoints = src->ParseInt();
|
||||
a1 = src->ParseInt();
|
||||
a2 = src->ParseInt();
|
||||
|
||||
w = new idWinding( numPoints );
|
||||
w->SetNumPoints( numPoints );
|
||||
for ( j = 0 ; j < numPoints ; j++ ) {
|
||||
src->Parse1DMatrix( 3, (*w)[j].ToFloatPtr() );
|
||||
// no texture coordinates
|
||||
(*w)[j][3] = 0;
|
||||
(*w)[j][4] = 0;
|
||||
}
|
||||
|
||||
// add the portal to a1
|
||||
p = (portal_t *)R_ClearedStaticAlloc( sizeof( *p ) );
|
||||
p->intoArea = a2;
|
||||
p->doublePortal = &doublePortals[i];
|
||||
p->w = w;
|
||||
p->w->GetPlane( p->plane );
|
||||
|
||||
p->next = portalAreas[a1].portals;
|
||||
portalAreas[a1].portals = p;
|
||||
|
||||
doublePortals[i].portals[0] = p;
|
||||
|
||||
// reverse it for a2
|
||||
p = (portal_t *)R_ClearedStaticAlloc( sizeof( *p ) );
|
||||
p->intoArea = a1;
|
||||
p->doublePortal = &doublePortals[i];
|
||||
p->w = w->Reverse();
|
||||
p->w->GetPlane( p->plane );
|
||||
|
||||
p->next = portalAreas[a2].portals;
|
||||
portalAreas[a2].portals = p;
|
||||
|
||||
doublePortals[i].portals[1] = p;
|
||||
}
|
||||
|
||||
src->ExpectTokenString( "}" );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idRenderWorldLocal::ParseNodes
|
||||
================
|
||||
*/
|
||||
void idRenderWorldLocal::ParseNodes( idLexer *src ) {
|
||||
int i;
|
||||
|
||||
src->ExpectTokenString( "{" );
|
||||
|
||||
numAreaNodes = src->ParseInt();
|
||||
if ( numAreaNodes < 0 ) {
|
||||
src->Error( "R_ParseNodes: bad numAreaNodes" );
|
||||
}
|
||||
areaNodes = (areaNode_t *)R_ClearedStaticAlloc( numAreaNodes * sizeof( areaNodes[0] ) );
|
||||
|
||||
for ( i = 0 ; i < numAreaNodes ; i++ ) {
|
||||
areaNode_t *node;
|
||||
|
||||
node = &areaNodes[i];
|
||||
|
||||
src->Parse1DMatrix( 4, node->plane.ToFloatPtr() );
|
||||
node->children[0] = src->ParseInt();
|
||||
node->children[1] = src->ParseInt();
|
||||
}
|
||||
|
||||
src->ExpectTokenString( "}" );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idRenderWorldLocal::CommonChildrenArea_r
|
||||
================
|
||||
*/
|
||||
int idRenderWorldLocal::CommonChildrenArea_r( areaNode_t *node ) {
|
||||
int nums[2];
|
||||
|
||||
for ( int i = 0 ; i < 2 ; i++ ) {
|
||||
if ( node->children[i] <= 0 ) {
|
||||
nums[i] = -1 - node->children[i];
|
||||
} else {
|
||||
nums[i] = CommonChildrenArea_r( &areaNodes[ node->children[i] ] );
|
||||
}
|
||||
}
|
||||
|
||||
// solid nodes will match any area
|
||||
if ( nums[0] == AREANUM_SOLID ) {
|
||||
nums[0] = nums[1];
|
||||
}
|
||||
if ( nums[1] == AREANUM_SOLID ) {
|
||||
nums[1] = nums[0];
|
||||
}
|
||||
|
||||
int common;
|
||||
if ( nums[0] == nums[1] ) {
|
||||
common = nums[0];
|
||||
} else {
|
||||
common = CHILDREN_HAVE_MULTIPLE_AREAS;
|
||||
}
|
||||
|
||||
node->commonChildrenArea = common;
|
||||
|
||||
return common;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderWorldLocal::ClearWorld
|
||||
|
||||
Sets up for a single area world
|
||||
=================
|
||||
*/
|
||||
void idRenderWorldLocal::ClearWorld() {
|
||||
numPortalAreas = 1;
|
||||
portalAreas = (portalArea_t *)R_ClearedStaticAlloc( sizeof( portalAreas[0] ) );
|
||||
areaScreenRect = (idScreenRect *) R_ClearedStaticAlloc( sizeof( idScreenRect ) );
|
||||
|
||||
SetupAreaRefs();
|
||||
|
||||
// even though we only have a single area, create a node
|
||||
// that has both children pointing at it so we don't need to
|
||||
//
|
||||
areaNodes = (areaNode_t *)R_ClearedStaticAlloc( sizeof( areaNodes[0] ) );
|
||||
areaNodes[0].plane[3] = 1;
|
||||
areaNodes[0].children[0] = -1;
|
||||
areaNodes[0].children[1] = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderWorldLocal::FreeDefs
|
||||
|
||||
dump all the interactions
|
||||
=================
|
||||
*/
|
||||
void idRenderWorldLocal::FreeDefs() {
|
||||
int i;
|
||||
|
||||
generateAllInteractionsCalled = false;
|
||||
|
||||
if ( interactionTable ) {
|
||||
R_StaticFree( interactionTable );
|
||||
interactionTable = NULL;
|
||||
}
|
||||
|
||||
// free all lightDefs
|
||||
for ( i = 0 ; i < lightDefs.Num() ; i++ ) {
|
||||
idRenderLightLocal *light;
|
||||
|
||||
light = lightDefs[i];
|
||||
if ( light && light->world == this ) {
|
||||
FreeLightDef( i );
|
||||
lightDefs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// free all entityDefs
|
||||
for ( i = 0 ; i < entityDefs.Num() ; i++ ) {
|
||||
idRenderEntityLocal *mod;
|
||||
|
||||
mod = entityDefs[i];
|
||||
if ( mod && mod->world == this ) {
|
||||
FreeEntityDef( i );
|
||||
entityDefs[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
idRenderWorldLocal::InitFromMap
|
||||
|
||||
A NULL or empty name will make a world without a map model, which
|
||||
is still useful for displaying a bare model
|
||||
=================
|
||||
*/
|
||||
bool idRenderWorldLocal::InitFromMap( const char *name ) {
|
||||
idLexer * src;
|
||||
idToken token;
|
||||
idStr filename;
|
||||
idRenderModel * lastModel;
|
||||
|
||||
// if this is an empty world, initialize manually
|
||||
if ( !name || !name[0] ) {
|
||||
FreeWorld();
|
||||
mapName.Clear();
|
||||
ClearWorld();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// load it
|
||||
filename = name;
|
||||
filename.SetFileExtension( PROC_FILE_EXT );
|
||||
|
||||
// if we are reloading the same map, check the timestamp
|
||||
// and try to skip all the work
|
||||
ID_TIME_T currentTimeStamp;
|
||||
fileSystem->ReadFile( filename, NULL, ¤tTimeStamp );
|
||||
|
||||
if ( name == mapName ) {
|
||||
if ( currentTimeStamp != FILE_NOT_FOUND_TIMESTAMP && currentTimeStamp == mapTimeStamp ) {
|
||||
common->Printf( "idRenderWorldLocal::InitFromMap: retaining existing map\n" );
|
||||
FreeDefs();
|
||||
TouchWorldModels();
|
||||
AddWorldModelEntities();
|
||||
ClearPortalStates();
|
||||
return true;
|
||||
}
|
||||
common->Printf( "idRenderWorldLocal::InitFromMap: timestamp has changed, reloading.\n" );
|
||||
}
|
||||
|
||||
FreeWorld();
|
||||
|
||||
src = new idLexer( filename, LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE );
|
||||
if ( !src->IsLoaded() ) {
|
||||
common->Printf( "idRenderWorldLocal::InitFromMap: %s not found\n", filename.c_str() );
|
||||
ClearWorld();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
mapName = name;
|
||||
mapTimeStamp = currentTimeStamp;
|
||||
|
||||
// if we are writing a demo, archive the load command
|
||||
if ( session->writeDemo ) {
|
||||
WriteLoadMap();
|
||||
}
|
||||
|
||||
if ( !src->ReadToken( &token ) || token.Icmp( PROC_FILE_ID ) ) {
|
||||
common->Printf( "idRenderWorldLocal::InitFromMap: bad id '%s' instead of '%s'\n", token.c_str(), PROC_FILE_ID );
|
||||
delete src;
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse the file
|
||||
while ( 1 ) {
|
||||
if ( !src->ReadToken( &token ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ( token == "model" ) {
|
||||
lastModel = ParseModel( src );
|
||||
|
||||
// add it to the model manager list
|
||||
renderModelManager->AddModel( lastModel );
|
||||
|
||||
// save it in the list to free when clearing this map
|
||||
localModels.Append( lastModel );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( token == "shadowModel" ) {
|
||||
lastModel = ParseShadowModel( src );
|
||||
|
||||
// add it to the model manager list
|
||||
renderModelManager->AddModel( lastModel );
|
||||
|
||||
// save it in the list to free when clearing this map
|
||||
localModels.Append( lastModel );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( token == "interAreaPortals" ) {
|
||||
ParseInterAreaPortals( src );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( token == "nodes" ) {
|
||||
ParseNodes( src );
|
||||
continue;
|
||||
}
|
||||
|
||||
src->Error( "idRenderWorldLocal::InitFromMap: bad token \"%s\"", token.c_str() );
|
||||
}
|
||||
|
||||
delete src;
|
||||
|
||||
// if it was a trivial map without any areas, create a single area
|
||||
if ( !numPortalAreas ) {
|
||||
ClearWorld();
|
||||
}
|
||||
|
||||
// find the points where we can early-our of reference pushing into the BSP tree
|
||||
CommonChildrenArea_r( &areaNodes[0] );
|
||||
|
||||
AddWorldModelEntities();
|
||||
ClearPortalStates();
|
||||
|
||||
// done!
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
idRenderWorldLocal::ClearPortalStates
|
||||
=====================
|
||||
*/
|
||||
void idRenderWorldLocal::ClearPortalStates() {
|
||||
int i, j;
|
||||
|
||||
// all portals start off open
|
||||
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
|
||||
doublePortals[i].blockingBits = PS_BLOCK_NONE;
|
||||
}
|
||||
|
||||
// flood fill all area connections
|
||||
for ( i = 0 ; i < numPortalAreas ; i++ ) {
|
||||
for ( j = 0 ; j < NUM_PORTAL_ATTRIBUTES ; j++ ) {
|
||||
connectedAreaNum++;
|
||||
FloodConnectedAreas( &portalAreas[i], j );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
idRenderWorldLocal::AddWorldModelEntities
|
||||
=====================
|
||||
*/
|
||||
void idRenderWorldLocal::AddWorldModelEntities() {
|
||||
int i;
|
||||
|
||||
// add the world model for each portal area
|
||||
// we can't just call AddEntityDef, because that would place the references
|
||||
// based on the bounding box, rather than explicitly into the correct area
|
||||
for ( i = 0 ; i < numPortalAreas ; i++ ) {
|
||||
idRenderEntityLocal *def;
|
||||
int index;
|
||||
|
||||
def = new idRenderEntityLocal;
|
||||
|
||||
// try and reuse a free spot
|
||||
index = entityDefs.FindNull();
|
||||
if ( index == -1 ) {
|
||||
index = entityDefs.Append(def);
|
||||
} else {
|
||||
entityDefs[index] = def;
|
||||
}
|
||||
|
||||
def->index = index;
|
||||
def->world = this;
|
||||
|
||||
def->parms.hModel = renderModelManager->FindModel( va("_area%i", i ) );
|
||||
if ( def->parms.hModel->IsDefaultModel() || !def->parms.hModel->IsStaticWorldModel() ) {
|
||||
common->Error( "idRenderWorldLocal::InitFromMap: bad area model lookup" );
|
||||
}
|
||||
|
||||
idRenderModel *hModel = def->parms.hModel;
|
||||
|
||||
for ( int j = 0; j < hModel->NumSurfaces(); j++ ) {
|
||||
const modelSurface_t *surf = hModel->Surface( j );
|
||||
|
||||
if ( surf->shader->GetName() == idStr( "textures/smf/portal_sky" ) ) {
|
||||
def->needsPortalSky = true;
|
||||
}
|
||||
}
|
||||
|
||||
def->referenceBounds = def->parms.hModel->Bounds();
|
||||
|
||||
def->parms.axis[0][0] = 1;
|
||||
def->parms.axis[1][1] = 1;
|
||||
def->parms.axis[2][2] = 1;
|
||||
|
||||
R_AxisToModelMatrix( def->parms.axis, def->parms.origin, def->modelMatrix );
|
||||
|
||||
// in case an explicit shader is used on the world, we don't
|
||||
// want it to have a 0 alpha or color
|
||||
def->parms.shaderParms[0] =
|
||||
def->parms.shaderParms[1] =
|
||||
def->parms.shaderParms[2] =
|
||||
def->parms.shaderParms[3] = 1;
|
||||
|
||||
AddEntityRefToArea( def, &portalAreas[i] );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
CheckAreaForPortalSky
|
||||
=====================
|
||||
*/
|
||||
bool idRenderWorldLocal::CheckAreaForPortalSky( int areaNum ) {
|
||||
areaReference_t *ref;
|
||||
|
||||
assert( areaNum >= 0 && areaNum < numPortalAreas );
|
||||
|
||||
for ( ref = portalAreas[areaNum].entityRefs.areaNext; ref->entity; ref = ref->areaNext ) {
|
||||
assert( ref->area == &portalAreas[areaNum] );
|
||||
|
||||
if ( ref->entity && ref->entity->needsPortalSky ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
262
neo/renderer/RenderWorld_local.h
Normal file
262
neo/renderer/RenderWorld_local.h
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __RENDERWORLDLOCAL_H__
|
||||
#define __RENDERWORLDLOCAL_H__
|
||||
|
||||
// assume any lightDef or entityDef index above this is an internal error
|
||||
const int LUDICROUS_INDEX = 10000;
|
||||
|
||||
|
||||
typedef struct portal_s {
|
||||
int intoArea; // area this portal leads to
|
||||
idWinding * w; // winding points have counter clockwise ordering seen this area
|
||||
idPlane plane; // view must be on the positive side of the plane to cross
|
||||
struct portal_s * next; // next portal of the area
|
||||
struct doublePortal_s * doublePortal;
|
||||
} portal_t;
|
||||
|
||||
|
||||
typedef struct doublePortal_s {
|
||||
struct portal_s * portals[2];
|
||||
int blockingBits; // PS_BLOCK_VIEW, PS_BLOCK_AIR, etc, set by doors that shut them off
|
||||
|
||||
// A portal will be considered closed if it is past the
|
||||
// fog-out point in a fog volume. We only support a single
|
||||
// fog volume over each portal.
|
||||
idRenderLightLocal * fogLight;
|
||||
struct doublePortal_s * nextFoggedPortal;
|
||||
} doublePortal_t;
|
||||
|
||||
|
||||
typedef struct portalArea_s {
|
||||
int areaNum;
|
||||
int connectedAreaNum[NUM_PORTAL_ATTRIBUTES]; // if two areas have matching connectedAreaNum, they are
|
||||
// not separated by a portal with the apropriate PS_BLOCK_* blockingBits
|
||||
int viewCount; // set by R_FindViewLightsAndEntities
|
||||
portal_t * portals; // never changes after load
|
||||
areaReference_t entityRefs; // head/tail of doubly linked list, may change
|
||||
areaReference_t lightRefs; // head/tail of doubly linked list, may change
|
||||
} portalArea_t;
|
||||
|
||||
|
||||
static const int CHILDREN_HAVE_MULTIPLE_AREAS = -2;
|
||||
static const int AREANUM_SOLID = -1;
|
||||
typedef struct {
|
||||
idPlane plane;
|
||||
int children[2]; // negative numbers are (-1 - areaNumber), 0 = solid
|
||||
int commonChildrenArea; // if all children are either solid or a single area,
|
||||
// this is the area number, else CHILDREN_HAVE_MULTIPLE_AREAS
|
||||
} areaNode_t;
|
||||
|
||||
|
||||
class idRenderWorldLocal : public idRenderWorld {
|
||||
public:
|
||||
idRenderWorldLocal();
|
||||
virtual ~idRenderWorldLocal();
|
||||
|
||||
virtual qhandle_t AddEntityDef( const renderEntity_t *re );
|
||||
virtual void UpdateEntityDef( qhandle_t entityHandle, const renderEntity_t *re );
|
||||
virtual void FreeEntityDef( qhandle_t entityHandle );
|
||||
virtual const renderEntity_t *GetRenderEntity( qhandle_t entityHandle ) const;
|
||||
|
||||
virtual qhandle_t AddLightDef( const renderLight_t *rlight );
|
||||
virtual void UpdateLightDef( qhandle_t lightHandle, const renderLight_t *rlight );
|
||||
virtual void FreeLightDef( qhandle_t lightHandle );
|
||||
virtual const renderLight_t *GetRenderLight( qhandle_t lightHandle ) const;
|
||||
|
||||
virtual bool CheckAreaForPortalSky( int areaNum );
|
||||
|
||||
virtual void GenerateAllInteractions();
|
||||
virtual void RegenerateWorld();
|
||||
|
||||
virtual void ProjectDecalOntoWorld( const idFixedWinding &winding, const idVec3 &projectionOrigin, const bool parallel, const float fadeDepth, const idMaterial *material, const int startTime );
|
||||
virtual void ProjectDecal( qhandle_t entityHandle, const idFixedWinding &winding, const idVec3 &projectionOrigin, const bool parallel, const float fadeDepth, const idMaterial *material, const int startTime );
|
||||
virtual void ProjectOverlay( qhandle_t entityHandle, const idPlane localTextureAxis[2], const idMaterial *material );
|
||||
virtual void RemoveDecals( qhandle_t entityHandle );
|
||||
|
||||
virtual void SetRenderView( const renderView_t *renderView );
|
||||
virtual void RenderScene( const renderView_t *renderView );
|
||||
|
||||
virtual int NumAreas( void ) const;
|
||||
virtual int PointInArea( const idVec3 &point ) const;
|
||||
virtual int BoundsInAreas( const idBounds &bounds, int *areas, int maxAreas ) const;
|
||||
virtual int NumPortalsInArea( int areaNum );
|
||||
virtual exitPortal_t GetPortal( int areaNum, int portalNum );
|
||||
|
||||
virtual guiPoint_t GuiTrace( qhandle_t entityHandle, const idVec3 start, const idVec3 end ) const;
|
||||
virtual bool ModelTrace( modelTrace_t &trace, qhandle_t entityHandle, const idVec3 &start, const idVec3 &end, const float radius ) const;
|
||||
virtual bool Trace( modelTrace_t &trace, const idVec3 &start, const idVec3 &end, const float radius, bool skipDynamic = true, bool skipPlayer = false ) const;
|
||||
virtual bool FastWorldTrace( modelTrace_t &trace, const idVec3 &start, const idVec3 &end ) const;
|
||||
|
||||
virtual void DebugClearLines( int time );
|
||||
virtual void DebugLine( const idVec4 &color, const idVec3 &start, const idVec3 &end, const int lifetime = 0, const bool depthTest = false );
|
||||
virtual void DebugArrow( const idVec4 &color, const idVec3 &start, const idVec3 &end, int size, const int lifetime = 0 );
|
||||
virtual void DebugWinding( const idVec4 &color, const idWinding &w, const idVec3 &origin, const idMat3 &axis, const int lifetime = 0, const bool depthTest = false );
|
||||
virtual void DebugCircle( const idVec4 &color, const idVec3 &origin, const idVec3 &dir, const float radius, const int numSteps, const int lifetime = 0, const bool depthTest = false );
|
||||
virtual void DebugSphere( const idVec4 &color, const idSphere &sphere, const int lifetime = 0, bool depthTest = false );
|
||||
virtual void DebugBounds( const idVec4 &color, const idBounds &bounds, const idVec3 &org = vec3_origin, const int lifetime = 0 );
|
||||
virtual void DebugBox( const idVec4 &color, const idBox &box, const int lifetime = 0 );
|
||||
virtual void DebugFrustum( const idVec4 &color, const idFrustum &frustum, const bool showFromOrigin = false, const int lifetime = 0 );
|
||||
virtual void DebugCone( const idVec4 &color, const idVec3 &apex, const idVec3 &dir, float radius1, float radius2, const int lifetime = 0 );
|
||||
virtual void DebugScreenRect( const idVec4 &color, const idScreenRect &rect, const viewDef_t *viewDef, const int lifetime = 0 );
|
||||
virtual void DebugAxis( const idVec3 &origin, const idMat3 &axis );
|
||||
|
||||
virtual void DebugClearPolygons( int time );
|
||||
virtual void DebugPolygon( const idVec4 &color, const idWinding &winding, const int lifeTime = 0, const bool depthTest = false );
|
||||
|
||||
virtual void DrawText( const char *text, const idVec3 &origin, float scale, const idVec4 &color, const idMat3 &viewAxis, const int align = 1, const int lifetime = 0, bool depthTest = false );
|
||||
|
||||
//-----------------------
|
||||
|
||||
idStr mapName; // ie: maps/tim_dm2.proc, written to demoFile
|
||||
ID_TIME_T mapTimeStamp; // for fast reloads of the same level
|
||||
|
||||
areaNode_t * areaNodes;
|
||||
int numAreaNodes;
|
||||
|
||||
portalArea_t * portalAreas;
|
||||
int numPortalAreas;
|
||||
int connectedAreaNum; // incremented every time a door portal state changes
|
||||
|
||||
idScreenRect * areaScreenRect;
|
||||
|
||||
doublePortal_t * doublePortals;
|
||||
int numInterAreaPortals;
|
||||
|
||||
idList<idRenderModel *> localModels;
|
||||
|
||||
idList<idRenderEntityLocal*> entityDefs;
|
||||
idList<idRenderLightLocal*> lightDefs;
|
||||
|
||||
idBlockAlloc<areaReference_t, 1024> areaReferenceAllocator;
|
||||
idBlockAlloc<idInteraction, 256> interactionAllocator;
|
||||
idBlockAlloc<areaNumRef_t, 1024> areaNumRefAllocator;
|
||||
|
||||
// all light / entity interactions are referenced here for fast lookup without
|
||||
// having to crawl the doubly linked lists. EnntityDefs are sequential for better
|
||||
// cache access, because the table is accessed by light in idRenderWorldLocal::CreateLightDefInteractions()
|
||||
// Growing this table is time consuming, so we add a pad value to the number
|
||||
// of entityDefs and lightDefs
|
||||
idInteraction ** interactionTable;
|
||||
int interactionTableWidth; // entityDefs
|
||||
int interactionTableHeight; // lightDefs
|
||||
|
||||
|
||||
bool generateAllInteractionsCalled;
|
||||
|
||||
//-----------------------
|
||||
// RenderWorld_load.cpp
|
||||
|
||||
idRenderModel * ParseModel( idLexer *src );
|
||||
idRenderModel * ParseShadowModel( idLexer *src );
|
||||
void SetupAreaRefs();
|
||||
void ParseInterAreaPortals( idLexer *src );
|
||||
void ParseNodes( idLexer *src );
|
||||
int CommonChildrenArea_r( areaNode_t *node );
|
||||
void FreeWorld();
|
||||
void ClearWorld();
|
||||
void FreeDefs();
|
||||
void TouchWorldModels( void );
|
||||
void AddWorldModelEntities();
|
||||
void ClearPortalStates();
|
||||
virtual bool InitFromMap( const char *mapName );
|
||||
|
||||
//--------------------------
|
||||
// RenderWorld_portals.cpp
|
||||
|
||||
idScreenRect ScreenRectFromWinding( const idWinding *w, viewEntity_t *space );
|
||||
bool PortalIsFoggedOut( const portal_t *p );
|
||||
void FloodViewThroughArea_r( const idVec3 origin, int areaNum, const struct portalStack_s *ps );
|
||||
void FlowViewThroughPortals( const idVec3 origin, int numPlanes, const idPlane *planes );
|
||||
void FloodLightThroughArea_r( idRenderLightLocal *light, int areaNum, const struct portalStack_s *ps );
|
||||
void FlowLightThroughPortals( idRenderLightLocal *light );
|
||||
areaNumRef_t * FloodFrustumAreas_r( const idFrustum &frustum, const int areaNum, const idBounds &bounds, areaNumRef_t *areas );
|
||||
areaNumRef_t * FloodFrustumAreas( const idFrustum &frustum, areaNumRef_t *areas );
|
||||
bool CullEntityByPortals( const idRenderEntityLocal *entity, const struct portalStack_s *ps );
|
||||
void AddAreaEntityRefs( int areaNum, const struct portalStack_s *ps );
|
||||
bool CullLightByPortals( const idRenderLightLocal *light, const struct portalStack_s *ps );
|
||||
void AddAreaLightRefs( int areaNum, const struct portalStack_s *ps );
|
||||
void AddAreaRefs( int areaNum, const struct portalStack_s *ps );
|
||||
void BuildConnectedAreas_r( int areaNum );
|
||||
void BuildConnectedAreas( void );
|
||||
void FindViewLightsAndEntities( void );
|
||||
|
||||
int NumPortals( void ) const;
|
||||
qhandle_t FindPortal( const idBounds &b ) const;
|
||||
void SetPortalState( qhandle_t portal, int blockingBits );
|
||||
int GetPortalState( qhandle_t portal );
|
||||
bool AreasAreConnected( int areaNum1, int areaNum2, portalConnection_t connection );
|
||||
void FloodConnectedAreas( portalArea_t *area, int portalAttributeIndex );
|
||||
idScreenRect & GetAreaScreenRect( int areaNum ) const { return areaScreenRect[areaNum]; }
|
||||
void ShowPortals();
|
||||
|
||||
//--------------------------
|
||||
// RenderWorld_demo.cpp
|
||||
|
||||
void StartWritingDemo( idDemoFile *demo );
|
||||
void StopWritingDemo();
|
||||
bool ProcessDemoCommand( idDemoFile *readDemo, renderView_t *demoRenderView, int *demoTimeOffset );
|
||||
|
||||
void WriteLoadMap();
|
||||
void WriteRenderView( const renderView_t *renderView );
|
||||
void WriteVisibleDefs( const viewDef_t *viewDef );
|
||||
void WriteFreeLight( qhandle_t handle );
|
||||
void WriteFreeEntity( qhandle_t handle );
|
||||
void WriteRenderLight( qhandle_t handle, const renderLight_t *light );
|
||||
void WriteRenderEntity( qhandle_t handle, const renderEntity_t *ent );
|
||||
void ReadRenderEntity();
|
||||
void ReadRenderLight();
|
||||
|
||||
|
||||
//--------------------------
|
||||
// RenderWorld.cpp
|
||||
|
||||
void ResizeInteractionTable();
|
||||
|
||||
void AddEntityRefToArea( idRenderEntityLocal *def, portalArea_t *area );
|
||||
void AddLightRefToArea( idRenderLightLocal *light, portalArea_t *area );
|
||||
|
||||
void RecurseProcBSP_r( modelTrace_t *results, int parentNodeNum, int nodeNum, float p1f, float p2f, const idVec3 &p1, const idVec3 &p2 ) const;
|
||||
|
||||
void BoundsInAreas_r( int nodeNum, const idBounds &bounds, int *areas, int *numAreas, int maxAreas ) const;
|
||||
|
||||
float DrawTextLength( const char *text, float scale, int len = 0 );
|
||||
|
||||
void FreeInteractions();
|
||||
|
||||
void PushVolumeIntoTree_r( idRenderEntityLocal *def, idRenderLightLocal *light, const idSphere *sphere, int numPoints, const idVec3 (*points), int nodeNum );
|
||||
|
||||
void PushVolumeIntoTree( idRenderEntityLocal *def, idRenderLightLocal *light, int numPoints, const idVec3 (*points) );
|
||||
|
||||
//-------------------------------
|
||||
// tr_light.c
|
||||
void CreateLightDefInteractions( idRenderLightLocal *ldef );
|
||||
};
|
||||
|
||||
#endif /* !__RENDERWORLDLOCAL_H__ */
|
||||
1073
neo/renderer/RenderWorld_portals.cpp
Normal file
1073
neo/renderer/RenderWorld_portals.cpp
Normal file
File diff suppressed because it is too large
Load Diff
563
neo/renderer/VertexCache.cpp
Normal file
563
neo/renderer/VertexCache.cpp
Normal file
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
|
||||
static const int FRAME_MEMORY_BYTES = 0x200000;
|
||||
static const int EXPAND_HEADERS = 1024;
|
||||
|
||||
idCVar idVertexCache::r_showVertexCache( "r_showVertexCache", "0", CVAR_INTEGER|CVAR_RENDERER, "" );
|
||||
idCVar idVertexCache::r_vertexBufferMegs( "r_vertexBufferMegs", "32", CVAR_INTEGER|CVAR_RENDERER, "" );
|
||||
|
||||
idVertexCache vertexCache;
|
||||
|
||||
/*
|
||||
==============
|
||||
R_ListVertexCache_f
|
||||
==============
|
||||
*/
|
||||
static void R_ListVertexCache_f( const idCmdArgs &args ) {
|
||||
vertexCache.List();
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
idVertexCache::ActuallyFree
|
||||
==============
|
||||
*/
|
||||
void idVertexCache::ActuallyFree( vertCache_t *block ) {
|
||||
if (!block) {
|
||||
common->Error( "idVertexCache Free: NULL pointer" );
|
||||
}
|
||||
|
||||
if ( block->user ) {
|
||||
// let the owner know we have purged it
|
||||
*block->user = NULL;
|
||||
block->user = NULL;
|
||||
}
|
||||
|
||||
// temp blocks are in a shared space that won't be freed
|
||||
if ( block->tag != TAG_TEMP ) {
|
||||
staticAllocTotal -= block->size;
|
||||
staticCountTotal--;
|
||||
|
||||
if ( block->vbo ) {
|
||||
#if 0 // this isn't really necessary, it will be reused soon enough
|
||||
// filling with zero length data is the equivalent of freeing
|
||||
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, block->vbo);
|
||||
qglBufferDataARB(GL_ARRAY_BUFFER_ARB, 0, 0, GL_DYNAMIC_DRAW_ARB);
|
||||
#endif
|
||||
} else if ( block->virtMem ) {
|
||||
Mem_Free( block->virtMem );
|
||||
block->virtMem = NULL;
|
||||
}
|
||||
}
|
||||
block->tag = TAG_FREE; // mark as free
|
||||
|
||||
// unlink stick it back on the free list
|
||||
block->next->prev = block->prev;
|
||||
block->prev->next = block->next;
|
||||
|
||||
#if 1
|
||||
// stick it on the front of the free list so it will be reused immediately
|
||||
block->next = freeStaticHeaders.next;
|
||||
block->prev = &freeStaticHeaders;
|
||||
#else
|
||||
// stick it on the back of the free list so it won't be reused soon (just for debugging)
|
||||
block->next = &freeStaticHeaders;
|
||||
block->prev = freeStaticHeaders.prev;
|
||||
#endif
|
||||
|
||||
block->next->prev = block;
|
||||
block->prev->next = block;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
idVertexCache::Position
|
||||
|
||||
this will be a real pointer with virtual memory,
|
||||
but it will be an int offset cast to a pointer with
|
||||
ARB_vertex_buffer_object
|
||||
|
||||
The ARB_vertex_buffer_object will be bound
|
||||
==============
|
||||
*/
|
||||
void *idVertexCache::Position( vertCache_t *buffer ) {
|
||||
if ( !buffer || buffer->tag == TAG_FREE ) {
|
||||
common->FatalError( "idVertexCache::Position: bad vertCache_t" );
|
||||
}
|
||||
|
||||
// the ARB vertex object just uses an offset
|
||||
if ( buffer->vbo ) {
|
||||
if ( r_showVertexCache.GetInteger() == 2 ) {
|
||||
if ( buffer->tag == TAG_TEMP ) {
|
||||
common->Printf( "GL_ARRAY_BUFFER_ARB = %i + %i (%i bytes)\n", buffer->vbo, buffer->offset, buffer->size );
|
||||
} else {
|
||||
common->Printf( "GL_ARRAY_BUFFER_ARB = %i (%i bytes)\n", buffer->vbo, buffer->size );
|
||||
}
|
||||
}
|
||||
if ( buffer->indexBuffer ) {
|
||||
qglBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, buffer->vbo );
|
||||
} else {
|
||||
qglBindBufferARB( GL_ARRAY_BUFFER_ARB, buffer->vbo );
|
||||
}
|
||||
return (void *)buffer->offset;
|
||||
}
|
||||
|
||||
// virtual memory is a real pointer
|
||||
return (void *)((byte *)buffer->virtMem + buffer->offset);
|
||||
}
|
||||
|
||||
void idVertexCache::UnbindIndex() {
|
||||
qglBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 );
|
||||
}
|
||||
|
||||
|
||||
//================================================================================
|
||||
|
||||
/*
|
||||
===========
|
||||
idVertexCache::Init
|
||||
===========
|
||||
*/
|
||||
void idVertexCache::Init() {
|
||||
cmdSystem->AddCommand( "listVertexCache", R_ListVertexCache_f, CMD_FL_RENDERER, "lists vertex cache" );
|
||||
|
||||
if ( r_vertexBufferMegs.GetInteger() < 8 ) {
|
||||
r_vertexBufferMegs.SetInteger( 8 );
|
||||
}
|
||||
|
||||
virtualMemory = false;
|
||||
|
||||
// use ARB_vertex_buffer_object unless explicitly disabled
|
||||
if( r_useVertexBuffers.GetInteger() && glConfig.ARBVertexBufferObjectAvailable ) {
|
||||
common->Printf( "using ARB_vertex_buffer_object memory\n" );
|
||||
} else {
|
||||
virtualMemory = true;
|
||||
r_useIndexBuffers.SetBool( false );
|
||||
common->Printf( "WARNING: vertex array range in virtual memory (SLOW)\n" );
|
||||
}
|
||||
|
||||
// initialize the cache memory blocks
|
||||
freeStaticHeaders.next = freeStaticHeaders.prev = &freeStaticHeaders;
|
||||
staticHeaders.next = staticHeaders.prev = &staticHeaders;
|
||||
freeDynamicHeaders.next = freeDynamicHeaders.prev = &freeDynamicHeaders;
|
||||
dynamicHeaders.next = dynamicHeaders.prev = &dynamicHeaders;
|
||||
deferredFreeList.next = deferredFreeList.prev = &deferredFreeList;
|
||||
|
||||
// set up the dynamic frame memory
|
||||
frameBytes = FRAME_MEMORY_BYTES;
|
||||
staticAllocTotal = 0;
|
||||
|
||||
byte *junk = (byte *)Mem_Alloc( frameBytes );
|
||||
for ( int i = 0 ; i < NUM_VERTEX_FRAMES ; i++ ) {
|
||||
allocatingTempBuffer = true; // force the alloc to use GL_STREAM_DRAW_ARB
|
||||
Alloc( junk, frameBytes, &tempBuffers[i] );
|
||||
allocatingTempBuffer = false;
|
||||
tempBuffers[i]->tag = TAG_FIXED;
|
||||
// unlink these from the static list, so they won't ever get purged
|
||||
tempBuffers[i]->next->prev = tempBuffers[i]->prev;
|
||||
tempBuffers[i]->prev->next = tempBuffers[i]->next;
|
||||
}
|
||||
Mem_Free( junk );
|
||||
|
||||
EndFrame();
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
idVertexCache::PurgeAll
|
||||
|
||||
Used when toggling vertex programs on or off, because
|
||||
the cached data isn't valid
|
||||
===========
|
||||
*/
|
||||
void idVertexCache::PurgeAll() {
|
||||
while( staticHeaders.next != &staticHeaders ) {
|
||||
ActuallyFree( staticHeaders.next );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
idVertexCache::Shutdown
|
||||
===========
|
||||
*/
|
||||
void idVertexCache::Shutdown() {
|
||||
// PurgeAll(); // !@#: also purge the temp buffers
|
||||
|
||||
headerAllocator.Shutdown();
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
idVertexCache::Alloc
|
||||
===========
|
||||
*/
|
||||
void idVertexCache::Alloc( void *data, int size, vertCache_t **buffer, bool indexBuffer ) {
|
||||
vertCache_t *block;
|
||||
|
||||
if ( size <= 0 ) {
|
||||
common->Error( "idVertexCache::Alloc: size = %i\n", size );
|
||||
}
|
||||
|
||||
// if we can't find anything, it will be NULL
|
||||
*buffer = NULL;
|
||||
|
||||
// if we don't have any remaining unused headers, allocate some more
|
||||
if ( freeStaticHeaders.next == &freeStaticHeaders ) {
|
||||
|
||||
for ( int i = 0; i < EXPAND_HEADERS; i++ ) {
|
||||
block = headerAllocator.Alloc();
|
||||
block->next = freeStaticHeaders.next;
|
||||
block->prev = &freeStaticHeaders;
|
||||
block->next->prev = block;
|
||||
block->prev->next = block;
|
||||
|
||||
if( !virtualMemory ) {
|
||||
qglGenBuffersARB( 1, & block->vbo );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move it from the freeStaticHeaders list to the staticHeaders list
|
||||
block = freeStaticHeaders.next;
|
||||
block->next->prev = block->prev;
|
||||
block->prev->next = block->next;
|
||||
block->next = staticHeaders.next;
|
||||
block->prev = &staticHeaders;
|
||||
block->next->prev = block;
|
||||
block->prev->next = block;
|
||||
|
||||
block->size = size;
|
||||
block->offset = 0;
|
||||
block->tag = TAG_USED;
|
||||
|
||||
// save data for debugging
|
||||
staticAllocThisFrame += block->size;
|
||||
staticCountThisFrame++;
|
||||
staticCountTotal++;
|
||||
staticAllocTotal += block->size;
|
||||
|
||||
// this will be set to zero when it is purged
|
||||
block->user = buffer;
|
||||
*buffer = block;
|
||||
|
||||
// allocation doesn't imply used-for-drawing, because at level
|
||||
// load time lots of things may be created, but they aren't
|
||||
// referenced by the GPU yet, and can be purged if needed.
|
||||
block->frameUsed = currentFrame - NUM_VERTEX_FRAMES;
|
||||
|
||||
block->indexBuffer = indexBuffer;
|
||||
|
||||
// copy the data
|
||||
if ( block->vbo ) {
|
||||
if ( indexBuffer ) {
|
||||
qglBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, block->vbo );
|
||||
qglBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, (GLsizeiptrARB)size, data, GL_STATIC_DRAW_ARB );
|
||||
} else {
|
||||
qglBindBufferARB( GL_ARRAY_BUFFER_ARB, block->vbo );
|
||||
if ( allocatingTempBuffer ) {
|
||||
qglBufferDataARB( GL_ARRAY_BUFFER_ARB, (GLsizeiptrARB)size, data, GL_STREAM_DRAW_ARB );
|
||||
} else {
|
||||
qglBufferDataARB( GL_ARRAY_BUFFER_ARB, (GLsizeiptrARB)size, data, GL_STATIC_DRAW_ARB );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
block->virtMem = Mem_Alloc( size );
|
||||
SIMDProcessor->Memcpy( block->virtMem, data, size );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
idVertexCache::Touch
|
||||
===========
|
||||
*/
|
||||
void idVertexCache::Touch( vertCache_t *block ) {
|
||||
if ( !block ) {
|
||||
common->Error( "idVertexCache Touch: NULL pointer" );
|
||||
}
|
||||
|
||||
if ( block->tag == TAG_FREE ) {
|
||||
common->FatalError( "idVertexCache Touch: freed pointer" );
|
||||
}
|
||||
if ( block->tag == TAG_TEMP ) {
|
||||
common->FatalError( "idVertexCache Touch: temporary pointer" );
|
||||
}
|
||||
|
||||
block->frameUsed = currentFrame;
|
||||
|
||||
// move to the head of the LRU list
|
||||
block->next->prev = block->prev;
|
||||
block->prev->next = block->next;
|
||||
|
||||
block->next = staticHeaders.next;
|
||||
block->prev = &staticHeaders;
|
||||
staticHeaders.next->prev = block;
|
||||
staticHeaders.next = block;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
idVertexCache::Free
|
||||
===========
|
||||
*/
|
||||
void idVertexCache::Free( vertCache_t *block ) {
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( block->tag == TAG_FREE ) {
|
||||
common->FatalError( "idVertexCache Free: freed pointer" );
|
||||
}
|
||||
if ( block->tag == TAG_TEMP ) {
|
||||
common->FatalError( "idVertexCache Free: temporary pointer" );
|
||||
}
|
||||
|
||||
// this block still can't be purged until the frame count has expired,
|
||||
// but it won't need to clear a user pointer when it is
|
||||
block->user = NULL;
|
||||
|
||||
block->next->prev = block->prev;
|
||||
block->prev->next = block->next;
|
||||
|
||||
block->next = deferredFreeList.next;
|
||||
block->prev = &deferredFreeList;
|
||||
deferredFreeList.next->prev = block;
|
||||
deferredFreeList.next = block;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
idVertexCache::AllocFrameTemp
|
||||
|
||||
A frame temp allocation must never be allowed to fail due to overflow.
|
||||
We can't simply sync with the GPU and overwrite what we have, because
|
||||
there may still be future references to dynamically created surfaces.
|
||||
===========
|
||||
*/
|
||||
vertCache_t *idVertexCache::AllocFrameTemp( void *data, int size ) {
|
||||
vertCache_t *block;
|
||||
|
||||
if ( size <= 0 ) {
|
||||
common->Error( "idVertexCache::AllocFrameTemp: size = %i\n", size );
|
||||
}
|
||||
|
||||
if ( dynamicAllocThisFrame + size > frameBytes ) {
|
||||
// if we don't have enough room in the temp block, allocate a static block,
|
||||
// but immediately free it so it will get freed at the next frame
|
||||
tempOverflow = true;
|
||||
Alloc( data, size, &block );
|
||||
Free( block);
|
||||
return block;
|
||||
}
|
||||
|
||||
// this data is just going on the shared dynamic list
|
||||
|
||||
// if we don't have any remaining unused headers, allocate some more
|
||||
if ( freeDynamicHeaders.next == &freeDynamicHeaders ) {
|
||||
|
||||
for ( int i = 0; i < EXPAND_HEADERS; i++ ) {
|
||||
block = headerAllocator.Alloc();
|
||||
block->next = freeDynamicHeaders.next;
|
||||
block->prev = &freeDynamicHeaders;
|
||||
block->next->prev = block;
|
||||
block->prev->next = block;
|
||||
}
|
||||
}
|
||||
|
||||
// move it from the freeDynamicHeaders list to the dynamicHeaders list
|
||||
block = freeDynamicHeaders.next;
|
||||
block->next->prev = block->prev;
|
||||
block->prev->next = block->next;
|
||||
block->next = dynamicHeaders.next;
|
||||
block->prev = &dynamicHeaders;
|
||||
block->next->prev = block;
|
||||
block->prev->next = block;
|
||||
|
||||
block->size = size;
|
||||
block->tag = TAG_TEMP;
|
||||
block->indexBuffer = false;
|
||||
block->offset = dynamicAllocThisFrame;
|
||||
dynamicAllocThisFrame += block->size;
|
||||
dynamicCountThisFrame++;
|
||||
block->user = NULL;
|
||||
block->frameUsed = 0;
|
||||
|
||||
// copy the data
|
||||
block->virtMem = tempBuffers[listNum]->virtMem;
|
||||
block->vbo = tempBuffers[listNum]->vbo;
|
||||
|
||||
if ( block->vbo ) {
|
||||
qglBindBufferARB( GL_ARRAY_BUFFER_ARB, block->vbo );
|
||||
qglBufferSubDataARB( GL_ARRAY_BUFFER_ARB, block->offset, (GLsizeiptrARB)size, data );
|
||||
} else {
|
||||
SIMDProcessor->Memcpy( (byte *)block->virtMem + block->offset, data, size );
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
idVertexCache::EndFrame
|
||||
===========
|
||||
*/
|
||||
void idVertexCache::EndFrame() {
|
||||
// display debug information
|
||||
if ( r_showVertexCache.GetBool() ) {
|
||||
int staticUseCount = 0;
|
||||
int staticUseSize = 0;
|
||||
|
||||
for ( vertCache_t *block = staticHeaders.next ; block != &staticHeaders ; block = block->next ) {
|
||||
if ( block->frameUsed == currentFrame ) {
|
||||
staticUseCount++;
|
||||
staticUseSize += block->size;
|
||||
}
|
||||
}
|
||||
|
||||
const char *frameOverflow = tempOverflow ? "(OVERFLOW)" : "";
|
||||
|
||||
common->Printf( "vertex dynamic:%i=%ik%s, static alloc:%i=%ik used:%i=%ik total:%i=%ik\n",
|
||||
dynamicCountThisFrame, dynamicAllocThisFrame/1024, frameOverflow,
|
||||
staticCountThisFrame, staticAllocThisFrame/1024,
|
||||
staticUseCount, staticUseSize/1024,
|
||||
staticCountTotal, staticAllocTotal/1024 );
|
||||
}
|
||||
|
||||
#if 0
|
||||
// if our total static count is above our working memory limit, start purging things
|
||||
while ( staticAllocTotal > r_vertexBufferMegs.GetInteger() * 1024 * 1024 ) {
|
||||
// free the least recently used
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
if( !virtualMemory ) {
|
||||
// unbind vertex buffers so normal virtual memory will be used in case
|
||||
// r_useVertexBuffers / r_useIndexBuffers
|
||||
qglBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
|
||||
qglBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 );
|
||||
}
|
||||
|
||||
|
||||
currentFrame = tr.frameCount;
|
||||
listNum = currentFrame % NUM_VERTEX_FRAMES;
|
||||
staticAllocThisFrame = 0;
|
||||
staticCountThisFrame = 0;
|
||||
dynamicAllocThisFrame = 0;
|
||||
dynamicCountThisFrame = 0;
|
||||
tempOverflow = false;
|
||||
|
||||
// free all the deferred free headers
|
||||
while( deferredFreeList.next != &deferredFreeList ) {
|
||||
ActuallyFree( deferredFreeList.next );
|
||||
}
|
||||
|
||||
// free all the frame temp headers
|
||||
vertCache_t *block = dynamicHeaders.next;
|
||||
if ( block != &dynamicHeaders ) {
|
||||
block->prev = &freeDynamicHeaders;
|
||||
dynamicHeaders.prev->next = freeDynamicHeaders.next;
|
||||
freeDynamicHeaders.next->prev = dynamicHeaders.prev;
|
||||
freeDynamicHeaders.next = block;
|
||||
|
||||
dynamicHeaders.next = dynamicHeaders.prev = &dynamicHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
idVertexCache::List
|
||||
=============
|
||||
*/
|
||||
void idVertexCache::List( void ) {
|
||||
int numActive = 0;
|
||||
int numDeferred = 0;
|
||||
int frameStatic = 0;
|
||||
int totalStatic = 0;
|
||||
int deferredSpace = 0;
|
||||
|
||||
vertCache_t *block;
|
||||
for ( block = staticHeaders.next ; block != &staticHeaders ; block = block->next) {
|
||||
numActive++;
|
||||
|
||||
totalStatic += block->size;
|
||||
if ( block->frameUsed == currentFrame ) {
|
||||
frameStatic += block->size;
|
||||
}
|
||||
}
|
||||
|
||||
int numFreeStaticHeaders = 0;
|
||||
for ( block = freeStaticHeaders.next ; block != &freeStaticHeaders ; block = block->next ) {
|
||||
numFreeStaticHeaders++;
|
||||
}
|
||||
|
||||
int numFreeDynamicHeaders = 0;
|
||||
for ( block = freeDynamicHeaders.next ; block != &freeDynamicHeaders ; block = block->next ) {
|
||||
numFreeDynamicHeaders++;
|
||||
}
|
||||
|
||||
common->Printf( "%i megs working set\n", r_vertexBufferMegs.GetInteger() );
|
||||
common->Printf( "%i dynamic temp buffers of %ik\n", NUM_VERTEX_FRAMES, frameBytes / 1024 );
|
||||
common->Printf( "%5i active static headers\n", numActive );
|
||||
common->Printf( "%5i free static headers\n", numFreeStaticHeaders );
|
||||
common->Printf( "%5i free dynamic headers\n", numFreeDynamicHeaders );
|
||||
|
||||
if ( !virtualMemory ) {
|
||||
common->Printf( "Vertex cache is in ARB_vertex_buffer_object memory (FAST).\n");
|
||||
} else {
|
||||
common->Printf( "Vertex cache is in virtual memory (SLOW)\n" );
|
||||
}
|
||||
|
||||
if ( r_useIndexBuffers.GetBool() ) {
|
||||
common->Printf( "Index buffers are accelerated.\n" );
|
||||
} else {
|
||||
common->Printf( "Index buffers are not used.\n" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
idVertexCache::IsFast
|
||||
|
||||
just for gfxinfo printing
|
||||
=============
|
||||
*/
|
||||
bool idVertexCache::IsFast() {
|
||||
if ( virtualMemory ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
143
neo/renderer/VertexCache.h
Normal file
143
neo/renderer/VertexCache.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// vertex cache calls should only be made by the front end
|
||||
|
||||
const int NUM_VERTEX_FRAMES = 2;
|
||||
|
||||
typedef enum {
|
||||
TAG_FREE,
|
||||
TAG_USED,
|
||||
TAG_FIXED, // for the temp buffers
|
||||
TAG_TEMP // in frame temp area, not static area
|
||||
} vertBlockTag_t;
|
||||
|
||||
typedef struct vertCache_s {
|
||||
GLuint vbo;
|
||||
void *virtMem; // only one of vbo / virtMem will be set
|
||||
bool indexBuffer; // holds indexes instead of vertexes
|
||||
|
||||
int offset;
|
||||
int size; // may be larger than the amount asked for, due
|
||||
// to round up and minimum fragment sizes
|
||||
int tag; // a tag of 0 is a free block
|
||||
struct vertCache_s ** user; // will be set to zero when purged
|
||||
struct vertCache_s *next, *prev; // may be on the static list or one of the frame lists
|
||||
int frameUsed; // it can't be purged if near the current frame
|
||||
} vertCache_t;
|
||||
|
||||
|
||||
class idVertexCache {
|
||||
public:
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
// just for gfxinfo printing
|
||||
bool IsFast();
|
||||
|
||||
// called when vertex programs are enabled or disabled, because
|
||||
// the cached data is no longer valid
|
||||
void PurgeAll();
|
||||
|
||||
// Tries to allocate space for the given data in fast vertex
|
||||
// memory, and copies it over.
|
||||
// Alloc does NOT do a touch, which allows purging of things
|
||||
// created at level load time even if a frame hasn't passed yet.
|
||||
// These allocations can be purged, which will zero the pointer.
|
||||
void Alloc( void *data, int bytes, vertCache_t **buffer, bool indexBuffer = false );
|
||||
|
||||
// This will be a real pointer with virtual memory,
|
||||
// but it will be an int offset cast to a pointer of ARB_vertex_buffer_object
|
||||
void * Position( vertCache_t *buffer );
|
||||
|
||||
// if r_useIndexBuffers is enabled, but you need to draw something without
|
||||
// an indexCache, this must be called to reset GL_ELEMENT_ARRAY_BUFFER_ARB
|
||||
void UnbindIndex();
|
||||
|
||||
// automatically freed at the end of the next frame
|
||||
// used for specular texture coordinates and gui drawing, which
|
||||
// will change every frame.
|
||||
// will return NULL if the vertex cache is completely full
|
||||
// As with Position(), this may not actually be a pointer you can access.
|
||||
vertCache_t * AllocFrameTemp( void *data, int bytes );
|
||||
|
||||
// notes that a buffer is used this frame, so it can't be purged
|
||||
// out from under the GPU
|
||||
void Touch( vertCache_t *buffer );
|
||||
|
||||
// this block won't have to zero a buffer pointer when it is purged,
|
||||
// but it must still wait for the frames to pass, in case the GPU
|
||||
// is still referencing it
|
||||
void Free( vertCache_t *buffer );
|
||||
|
||||
// updates the counter for determining which temp space to use
|
||||
// and which blocks can be purged
|
||||
// Also prints debugging info when enabled
|
||||
void EndFrame();
|
||||
|
||||
// listVertexCache calls this
|
||||
void List();
|
||||
|
||||
private:
|
||||
void InitMemoryBlocks( int size );
|
||||
void ActuallyFree( vertCache_t *block );
|
||||
|
||||
static idCVar r_showVertexCache;
|
||||
static idCVar r_vertexBufferMegs;
|
||||
|
||||
int staticCountTotal;
|
||||
int staticAllocTotal; // for end of frame purging
|
||||
|
||||
int staticAllocThisFrame; // debug counter
|
||||
int staticCountThisFrame;
|
||||
int dynamicAllocThisFrame;
|
||||
int dynamicCountThisFrame;
|
||||
|
||||
int currentFrame; // for purgable block tracking
|
||||
int listNum; // currentFrame % NUM_VERTEX_FRAMES, determines which tempBuffers to use
|
||||
|
||||
bool virtualMemory; // not fast stuff
|
||||
|
||||
bool allocatingTempBuffer; // force GL_STREAM_DRAW_ARB
|
||||
|
||||
vertCache_t *tempBuffers[NUM_VERTEX_FRAMES]; // allocated at startup
|
||||
bool tempOverflow; // had to alloc a temp in static memory
|
||||
|
||||
idBlockAlloc<vertCache_t,1024> headerAllocator;
|
||||
|
||||
vertCache_t freeStaticHeaders; // head of doubly linked list
|
||||
vertCache_t freeDynamicHeaders; // head of doubly linked list
|
||||
vertCache_t dynamicHeaders; // head of doubly linked list
|
||||
vertCache_t deferredFreeList; // head of doubly linked list
|
||||
vertCache_t staticHeaders; // head of doubly linked list in MRU order,
|
||||
// staticHeaders.next is most recently used
|
||||
|
||||
int frameBytes; // for each of NUM_VERTEX_FRAMES frames
|
||||
};
|
||||
|
||||
extern idVertexCache vertexCache;
|
||||
423
neo/renderer/cg_explicit.cpp
Normal file
423
neo/renderer/cg_explicit.cpp
Normal file
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
//#include <windows.h>
|
||||
//#include <GL/gl.h>
|
||||
#include "cg_explicit.h"
|
||||
|
||||
PFNCGCREATECONTEXTPROC cgCreateContext;
|
||||
PFNCGDESTROYCONTEXTPROC cgDestroyContext;
|
||||
PFNCGISCONTEXTPROC cgIsContext;
|
||||
PFNCGGETLASTLISTINGPROC cgGetLastListing;
|
||||
PFNCGCREATEPROGRAMPROC cgCreateProgram;
|
||||
PFNCGCREATEPROGRAMFROMFILEPROC cgCreateProgramFromFile;
|
||||
PFNCGCOPYPROGRAMPROC cgCopyProgram;
|
||||
PFNCGDESTROYPROGRAMPROC cgDestroyProgram;
|
||||
PFNCGGETFIRSTPROGRAMPROC cgGetFirstProgram;
|
||||
PFNCGGETNEXTPROGRAMPROC cgGetNextProgram;
|
||||
PFNCGGETPROGRAMCONTEXTPROC cgGetProgramContext;
|
||||
PFNCGISPROGRAMPROC cgIsProgram;
|
||||
PFNCGCOMPILEPROGRAMPROC cgCompileProgram;
|
||||
PFNCGISPROGRAMCOMPILEDPROC cgIsProgramCompiled;
|
||||
PFNCGGETPROGRAMSTRINGPROC cgGetProgramString;
|
||||
PFNCGGETPROGRAMPROFILEPROC cgGetProgramProfile;
|
||||
PFNCGGETNAMEDPARAMETERPROC cgGetNamedParameter;
|
||||
PFNCGGETFIRSTPARAMETERPROC cgGetFirstParameter;
|
||||
PFNCGGETNEXTPARAMETERPROC cgGetNextParameter;
|
||||
PFNCGGETFIRSTLEAFPARAMETERPROC cgGetFirstLeafParameter;
|
||||
PFNCGGETNEXTLEAFPARAMETERPROC cgGetNextLeafParameter;
|
||||
PFNCGGETFIRSTSTRUCTPARAMETERPROC cgGetFirstStructParameter;
|
||||
PFNCGGETFIRSTDEPENDENTPARAMETERPROC cgGetFirstDependentParameter;
|
||||
PFNCGGETARRAYPARAMETERPROC cgGetArrayParameter;
|
||||
PFNCGGETARRAYDIMENSIONPROC cgGetArrayDimension;
|
||||
PFNCGGETARRAYSIZEPROC cgGetArraySize;
|
||||
PFNCGGETPARAMETERPROGRAMPROC cgGetParameterProgram;
|
||||
PFNCGISPARAMETERPROC cgIsParameter;
|
||||
PFNCGGETPARAMETERNAMEPROC cgGetParameterName;
|
||||
PFNCGGETPARAMETERTYPEPROC cgGetParameterType;
|
||||
PFNCGGETPARAMETERSEMANTICPROC cgGetParameterSemantic;
|
||||
PFNCGGETPARAMETERRESOURCEPROC cgGetParameterResource;
|
||||
PFNCGGETPARAMETERBASERESOURCEPROC cgGetParameterBaseResource;
|
||||
PFNCGGETPARAMETERRESOURCEINDEXPROC cgGetParameterResourceIndex;
|
||||
PFNCGGETPARAMETERVARIABILITYPROC cgGetParameterVariability;
|
||||
PFNCGGETPARAMETERDIRECTIONPROC cgGetParameterDirection;
|
||||
PFNCGISPARAMETERREFERENCEDPROC cgIsParameterReferenced;
|
||||
PFNCGGETPARAMETERVALUESPROC cgGetParameterValues;
|
||||
PFNCGGETTYPESTRINGPROC cgGetTypeString;
|
||||
PFNCGGETTYPEPROC cgGetType;
|
||||
PFNCGGETRESOURCESTRINGPROC cgGetResourceString;
|
||||
PFNCGGETRESOURCEPROC cgGetResource;
|
||||
PFNCGGETPROFILESTRINGPROC cgGetProfileString;
|
||||
PFNCGGETPROFILEPROC cgGetProfile;
|
||||
PFNCGGETERRORPROC cgGetError;
|
||||
PFNCGGETERRORSTRINGPROC cgGetErrorString;
|
||||
PFNCGSETERRORCALLBACKPROC cgSetErrorCallback;
|
||||
PFNCGGETERRORCALLBACKPROC cgGetErrorCallback;
|
||||
PFNCGGLISPROFILESUPPORTEDPROC cgGLIsProfileSupported;
|
||||
PFNCGGLENABLEPROFILEPROC cgGLEnableProfile;
|
||||
PFNCGGLDISABLEPROFILEPROC cgGLDisableProfile;
|
||||
PFNCGGLGETLATESTPROFILEPROC cgGLGetLatestProfile;
|
||||
PFNCGGLSETOPTIMALOPTIONSPROC cgGLSetOptimalOptions;
|
||||
PFNCGGLLOADPROGRAMPROC cgGLLoadProgram;
|
||||
PFNCGGLBINDPROGRAMPROC cgGLBindProgram;
|
||||
PFNCGGLSETPARAMETER1FPROC cgGLSetParameter1f;
|
||||
PFNCGGLSETPARAMETER2FPROC cgGLSetParameter2f;
|
||||
PFNCGGLSETPARAMETER3FPROC cgGLSetParameter3f;
|
||||
PFNCGGLSETPARAMETER4FPROC cgGLSetParameter4f;
|
||||
PFNCGGLSETPARAMETER1FVPROC cgGLSetParameter1fv;
|
||||
PFNCGGLSETPARAMETER2FVPROC cgGLSetParameter2fv;
|
||||
PFNCGGLSETPARAMETER3FVPROC cgGLSetParameter3fv;
|
||||
PFNCGGLSETPARAMETER4FVPROC cgGLSetParameter4fv;
|
||||
PFNCGGLSETPARAMETER1DPROC cgGLSetParameter1d;
|
||||
PFNCGGLSETPARAMETER2DPROC cgGLSetParameter2d;
|
||||
PFNCGGLSETPARAMETER3DPROC cgGLSetParameter3d;
|
||||
PFNCGGLSETPARAMETER4DPROC cgGLSetParameter4d;
|
||||
PFNCGGLSETPARAMETER1DVPROC cgGLSetParameter1dv;
|
||||
PFNCGGLSETPARAMETER2DVPROC cgGLSetParameter2dv;
|
||||
PFNCGGLSETPARAMETER3DVPROC cgGLSetParameter3dv;
|
||||
PFNCGGLSETPARAMETER4DVPROC cgGLSetParameter4dv;
|
||||
PFNCGGLGETPARAMETER1FPROC cgGLGetParameter1f;
|
||||
PFNCGGLGETPARAMETER2FPROC cgGLGetParameter2f;
|
||||
PFNCGGLGETPARAMETER3FPROC cgGLGetParameter3f;
|
||||
PFNCGGLGETPARAMETER4FPROC cgGLGetParameter4f;
|
||||
PFNCGGLGETPARAMETER1DPROC cgGLGetParameter1d;
|
||||
PFNCGGLGETPARAMETER2DPROC cgGLGetParameter2d;
|
||||
PFNCGGLGETPARAMETER3DPROC cgGLGetParameter3d;
|
||||
PFNCGGLGETPARAMETER4DPROC cgGLGetParameter4d;
|
||||
PFNCGGLSETPARAMETERARRAY1FPROC cgGLSetParameterArray1f;
|
||||
PFNCGGLSETPARAMETERARRAY2FPROC cgGLSetParameterArray2f;
|
||||
PFNCGGLSETPARAMETERARRAY3FPROC cgGLSetParameterArray3f;
|
||||
PFNCGGLSETPARAMETERARRAY4FPROC cgGLSetParameterArray4f;
|
||||
PFNCGGLSETPARAMETERARRAY1DPROC cgGLSetParameterArray1d;
|
||||
PFNCGGLSETPARAMETERARRAY2DPROC cgGLSetParameterArray2d;
|
||||
PFNCGGLSETPARAMETERARRAY3DPROC cgGLSetParameterArray3d;
|
||||
PFNCGGLSETPARAMETERARRAY4DPROC cgGLSetParameterArray4d;
|
||||
PFNCGGLGETPARAMETERARRAY1FPROC cgGLGetParameterArray1f;
|
||||
PFNCGGLGETPARAMETERARRAY2FPROC cgGLGetParameterArray2f;
|
||||
PFNCGGLGETPARAMETERARRAY3FPROC cgGLGetParameterArray3f;
|
||||
PFNCGGLGETPARAMETERARRAY4FPROC cgGLGetParameterArray4f;
|
||||
PFNCGGLGETPARAMETERARRAY1DPROC cgGLGetParameterArray1d;
|
||||
PFNCGGLGETPARAMETERARRAY2DPROC cgGLGetParameterArray2d;
|
||||
PFNCGGLGETPARAMETERARRAY3DPROC cgGLGetParameterArray3d;
|
||||
PFNCGGLGETPARAMETERARRAY4DPROC cgGLGetParameterArray4d;
|
||||
PFNCGGLSETPARAMETERPOINTERPROC cgGLSetParameterPointer;
|
||||
PFNCGGLENABLECLIENTSTATEPROC cgGLEnableClientState;
|
||||
PFNCGGLDISABLECLIENTSTATEPROC cgGLDisableClientState;
|
||||
PFNCGGLSETMATRIXPARAMETERDRPROC cgGLSetMatrixParameterdr;
|
||||
PFNCGGLSETMATRIXPARAMETERFRPROC cgGLSetMatrixParameterfr;
|
||||
PFNCGGLSETMATRIXPARAMETERDCPROC cgGLSetMatrixParameterdc;
|
||||
PFNCGGLSETMATRIXPARAMETERFCPROC cgGLSetMatrixParameterfc;
|
||||
PFNCGGLGETMATRIXPARAMETERDRPROC cgGLGetMatrixParameterdr;
|
||||
PFNCGGLGETMATRIXPARAMETERFRPROC cgGLGetMatrixParameterfr;
|
||||
PFNCGGLGETMATRIXPARAMETERDCPROC cgGLGetMatrixParameterdc;
|
||||
PFNCGGLGETMATRIXPARAMETERFCPROC cgGLGetMatrixParameterfc;
|
||||
PFNCGGLSETSTATEMATRIXPARAMETERPROC cgGLSetStateMatrixParameter;
|
||||
PFNCGGLSETMATRIXPARAMETERARRAYFCPROC cgGLSetMatrixParameterArrayfc;
|
||||
PFNCGGLSETMATRIXPARAMETERARRAYFRPROC cgGLSetMatrixParameterArrayfr;
|
||||
PFNCGGLSETMATRIXPARAMETERARRAYDCPROC cgGLSetMatrixParameterArraydc;
|
||||
PFNCGGLSETMATRIXPARAMETERARRAYDRPROC cgGLSetMatrixParameterArraydr;
|
||||
PFNCGGLGETMATRIXPARAMETERARRAYFCPROC cgGLGetMatrixParameterArrayfc;
|
||||
PFNCGGLGETMATRIXPARAMETERARRAYFRPROC cgGLGetMatrixParameterArrayfr;
|
||||
PFNCGGLGETMATRIXPARAMETERARRAYDCPROC cgGLGetMatrixParameterArraydc;
|
||||
PFNCGGLGETMATRIXPARAMETERARRAYDRPROC cgGLGetMatrixParameterArraydr;
|
||||
PFNCGGLSETTEXTUREPARAMETERPROC cgGLSetTextureParameter;
|
||||
PFNCGGLGETTEXTUREPARAMETERPROC cgGLGetTextureParameter;
|
||||
PFNCGGLENABLETEXTUREPARAMETERPROC cgGLEnableTextureParameter;
|
||||
PFNCGGLDISABLETEXTUREPARAMETERPROC cgGLDisableTextureParameter;
|
||||
PFNCGGLGETTEXTUREENUMPROC cgGLGetTextureEnum;
|
||||
|
||||
#ifndef _WIN32
|
||||
bool init_explicit_Cg()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
bool init_explicit_Cg()
|
||||
{
|
||||
HMODULE hmod;
|
||||
int failed = 0;
|
||||
|
||||
hmod = LoadLibrary("cg.dll");
|
||||
|
||||
if(0 == (cgCreateContext = (PFNCGCREATECONTEXTPROC)GetProcAddress( hmod, "cgCreateContext" )))
|
||||
failed++;
|
||||
if(0 == (cgDestroyContext = (PFNCGDESTROYCONTEXTPROC)GetProcAddress( hmod, "cgDestroyContext" )))
|
||||
failed++;
|
||||
if(0 == (cgIsContext = (PFNCGISCONTEXTPROC)GetProcAddress( hmod, "cgIsContext" )))
|
||||
failed++;
|
||||
if(0 == (cgGetLastListing = (PFNCGGETLASTLISTINGPROC)GetProcAddress( hmod, "cgGetLastListing" )))
|
||||
failed++;
|
||||
if(0 == (cgCreateProgram = (PFNCGCREATEPROGRAMPROC)GetProcAddress( hmod, "cgCreateProgram" )))
|
||||
failed++;
|
||||
if(0 == (cgCreateProgramFromFile = (PFNCGCREATEPROGRAMFROMFILEPROC)GetProcAddress( hmod, "cgCreateProgramFromFile" )))
|
||||
failed++;
|
||||
if(0 == (cgCopyProgram = (PFNCGCOPYPROGRAMPROC)GetProcAddress( hmod, "cgCopyProgram" )))
|
||||
failed++;
|
||||
if(0 == (cgDestroyProgram = (PFNCGDESTROYPROGRAMPROC)GetProcAddress( hmod, "cgDestroyProgram" )))
|
||||
failed++;
|
||||
if(0 == (cgGetFirstProgram = (PFNCGGETFIRSTPROGRAMPROC)GetProcAddress( hmod, "cgGetFirstProgram" )))
|
||||
failed++;
|
||||
if(0 == (cgGetNextProgram = (PFNCGGETNEXTPROGRAMPROC)GetProcAddress( hmod, "cgGetNextProgram" )))
|
||||
failed++;
|
||||
if(0 == (cgGetProgramContext = (PFNCGGETPROGRAMCONTEXTPROC)GetProcAddress( hmod, "cgGetProgramContext" )))
|
||||
failed++;
|
||||
if(0 == (cgIsProgram = (PFNCGISPROGRAMPROC)GetProcAddress( hmod, "cgIsProgram" )))
|
||||
failed++;
|
||||
if(0 == (cgCompileProgram = (PFNCGCOMPILEPROGRAMPROC)GetProcAddress( hmod, "cgCompileProgram" )))
|
||||
failed++;
|
||||
if(0 == (cgIsProgramCompiled = (PFNCGISPROGRAMCOMPILEDPROC)GetProcAddress( hmod, "cgIsProgramCompiled" )))
|
||||
failed++;
|
||||
if(0 == (cgGetProgramString = (PFNCGGETPROGRAMSTRINGPROC)GetProcAddress( hmod, "cgGetProgramString" )))
|
||||
failed++;
|
||||
if(0 == (cgGetProgramProfile = (PFNCGGETPROGRAMPROFILEPROC)GetProcAddress( hmod, "cgGetProgramProfile" )))
|
||||
failed++;
|
||||
if(0 == (cgGetNamedParameter = (PFNCGGETNAMEDPARAMETERPROC)GetProcAddress( hmod, "cgGetNamedParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGetFirstParameter = (PFNCGGETFIRSTPARAMETERPROC)GetProcAddress( hmod, "cgGetFirstParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGetNextParameter = (PFNCGGETNEXTPARAMETERPROC)GetProcAddress( hmod, "cgGetNextParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGetFirstLeafParameter = (PFNCGGETFIRSTLEAFPARAMETERPROC)GetProcAddress( hmod, "cgGetFirstLeafParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGetNextLeafParameter = (PFNCGGETNEXTLEAFPARAMETERPROC)GetProcAddress( hmod, "cgGetNextLeafParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGetFirstStructParameter = (PFNCGGETFIRSTSTRUCTPARAMETERPROC)GetProcAddress( hmod, "cgGetFirstStructParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGetFirstDependentParameter = (PFNCGGETFIRSTDEPENDENTPARAMETERPROC)GetProcAddress( hmod, "cgGetFirstDependentParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGetArrayParameter = (PFNCGGETARRAYPARAMETERPROC)GetProcAddress( hmod, "cgGetArrayParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGetArrayDimension = (PFNCGGETARRAYDIMENSIONPROC)GetProcAddress( hmod, "cgGetArrayDimension" )))
|
||||
failed++;
|
||||
if(0 == (cgGetArraySize = (PFNCGGETARRAYSIZEPROC)GetProcAddress( hmod, "cgGetArraySize" )))
|
||||
failed++;
|
||||
if(0 == (cgGetParameterProgram = (PFNCGGETPARAMETERPROGRAMPROC)GetProcAddress( hmod, "cgGetParameterProgram" )))
|
||||
failed++;
|
||||
if(0 == (cgIsParameter = (PFNCGISPARAMETERPROC)GetProcAddress( hmod, "cgIsParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGetParameterName = (PFNCGGETPARAMETERNAMEPROC)GetProcAddress( hmod, "cgGetParameterName" )))
|
||||
failed++;
|
||||
if(0 == (cgGetParameterType = (PFNCGGETPARAMETERTYPEPROC)GetProcAddress( hmod, "cgGetParameterType" )))
|
||||
failed++;
|
||||
if(0 == (cgGetParameterSemantic = (PFNCGGETPARAMETERSEMANTICPROC)GetProcAddress( hmod, "cgGetParameterSemantic" )))
|
||||
failed++;
|
||||
if(0 == (cgGetParameterResource = (PFNCGGETPARAMETERRESOURCEPROC)GetProcAddress( hmod, "cgGetParameterResource" )))
|
||||
failed++;
|
||||
if(0 == (cgGetParameterBaseResource = (PFNCGGETPARAMETERBASERESOURCEPROC)GetProcAddress( hmod, "cgGetParameterBaseResource" )))
|
||||
failed++;
|
||||
if(0 == (cgGetParameterResourceIndex = (PFNCGGETPARAMETERRESOURCEINDEXPROC)GetProcAddress( hmod, "cgGetParameterResourceIndex" )))
|
||||
failed++;
|
||||
if(0 == (cgGetParameterVariability = (PFNCGGETPARAMETERVARIABILITYPROC)GetProcAddress( hmod, "cgGetParameterVariability" )))
|
||||
failed++;
|
||||
if(0 == (cgGetParameterDirection = (PFNCGGETPARAMETERDIRECTIONPROC)GetProcAddress( hmod, "cgGetParameterDirection" )))
|
||||
failed++;
|
||||
if(0 == (cgIsParameterReferenced = (PFNCGISPARAMETERREFERENCEDPROC)GetProcAddress( hmod, "cgIsParameterReferenced" )))
|
||||
failed++;
|
||||
if(0 == (cgGetParameterValues = (PFNCGGETPARAMETERVALUESPROC)GetProcAddress( hmod, "cgGetParameterValues" )))
|
||||
failed++;
|
||||
if(0 == (cgGetTypeString = (PFNCGGETTYPESTRINGPROC)GetProcAddress( hmod, "cgGetTypeString" )))
|
||||
failed++;
|
||||
if(0 == (cgGetType = (PFNCGGETTYPEPROC)GetProcAddress( hmod, "cgGetType" )))
|
||||
failed++;
|
||||
if(0 == (cgGetResourceString = (PFNCGGETRESOURCESTRINGPROC)GetProcAddress( hmod, "cgGetResourceString" )))
|
||||
failed++;
|
||||
if(0 == (cgGetResource = (PFNCGGETRESOURCEPROC)GetProcAddress( hmod, "cgGetResource" )))
|
||||
failed++;
|
||||
if(0 == (cgGetProfileString = (PFNCGGETPROFILESTRINGPROC)GetProcAddress( hmod, "cgGetProfileString" )))
|
||||
failed++;
|
||||
if(0 == (cgGetProfile = (PFNCGGETPROFILEPROC)GetProcAddress( hmod, "cgGetProfile" )))
|
||||
failed++;
|
||||
if(0 == (cgGetError = (PFNCGGETERRORPROC)GetProcAddress( hmod, "cgGetError" )))
|
||||
failed++;
|
||||
if(0 == (cgGetErrorString = (PFNCGGETERRORSTRINGPROC)GetProcAddress( hmod, "cgGetErrorString" )))
|
||||
failed++;
|
||||
if(0 == (cgSetErrorCallback = (PFNCGSETERRORCALLBACKPROC)GetProcAddress( hmod, "cgSetErrorCallback" )))
|
||||
failed++;
|
||||
if(0 == (cgGetErrorCallback = (PFNCGGETERRORCALLBACKPROC)GetProcAddress( hmod, "cgGetErrorCallback" )))
|
||||
failed++;
|
||||
|
||||
|
||||
|
||||
hmod = LoadLibrary("cgGL.dll");
|
||||
|
||||
|
||||
|
||||
if(0 == (cgGLIsProfileSupported = (PFNCGGLISPROFILESUPPORTEDPROC)GetProcAddress( hmod, "cgGLIsProfileSupported" )))
|
||||
failed++;
|
||||
if(0 == (cgGLEnableProfile = (PFNCGGLENABLEPROFILEPROC)GetProcAddress( hmod, "cgGLEnableProfile" )))
|
||||
failed++;
|
||||
if(0 == (cgGLDisableProfile = (PFNCGGLDISABLEPROFILEPROC)GetProcAddress( hmod, "cgGLDisableProfile" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetLatestProfile = (PFNCGGLGETLATESTPROFILEPROC)GetProcAddress( hmod, "cgGLGetLatestProfile" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetOptimalOptions = (PFNCGGLSETOPTIMALOPTIONSPROC)GetProcAddress( hmod, "cgGLSetOptimalOptions" )))
|
||||
failed++;
|
||||
if(0 == (cgGLLoadProgram = (PFNCGGLLOADPROGRAMPROC)GetProcAddress( hmod, "cgGLLoadProgram" )))
|
||||
failed++;
|
||||
if(0 == (cgGLBindProgram = (PFNCGGLBINDPROGRAMPROC)GetProcAddress( hmod, "cgGLBindProgram" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter1f = (PFNCGGLSETPARAMETER1FPROC)GetProcAddress( hmod, "cgGLSetParameter1f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter2f = (PFNCGGLSETPARAMETER2FPROC)GetProcAddress( hmod, "cgGLSetParameter2f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter3f = (PFNCGGLSETPARAMETER3FPROC)GetProcAddress( hmod, "cgGLSetParameter3f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter4f = (PFNCGGLSETPARAMETER4FPROC)GetProcAddress( hmod, "cgGLSetParameter4f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter1fv = (PFNCGGLSETPARAMETER1FVPROC)GetProcAddress( hmod, "cgGLSetParameter1fv" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter2fv = (PFNCGGLSETPARAMETER2FVPROC)GetProcAddress( hmod, "cgGLSetParameter2fv" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter3fv = (PFNCGGLSETPARAMETER3FVPROC)GetProcAddress( hmod, "cgGLSetParameter3fv" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter4fv = (PFNCGGLSETPARAMETER4FVPROC)GetProcAddress( hmod, "cgGLSetParameter4fv" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter1d = (PFNCGGLSETPARAMETER1DPROC)GetProcAddress( hmod, "cgGLSetParameter1d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter2d = (PFNCGGLSETPARAMETER2DPROC)GetProcAddress( hmod, "cgGLSetParameter2d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter3d = (PFNCGGLSETPARAMETER3DPROC)GetProcAddress( hmod, "cgGLSetParameter3d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter4d = (PFNCGGLSETPARAMETER4DPROC)GetProcAddress( hmod, "cgGLSetParameter4d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter1dv = (PFNCGGLSETPARAMETER1DVPROC)GetProcAddress( hmod, "cgGLSetParameter1dv" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter2dv = (PFNCGGLSETPARAMETER2DVPROC)GetProcAddress( hmod, "cgGLSetParameter2dv" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter3dv = (PFNCGGLSETPARAMETER3DVPROC)GetProcAddress( hmod, "cgGLSetParameter3dv" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter4dv = (PFNCGGLSETPARAMETER4DVPROC)GetProcAddress( hmod, "cgGLSetParameter4dv" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameter4dv = (PFNCGGLSETPARAMETER4DVPROC)GetProcAddress( hmod, "cgGLSetParameter4dv" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameter1f = (PFNCGGLGETPARAMETER1FPROC)GetProcAddress( hmod, "cgGLGetParameter1f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameter2f = (PFNCGGLGETPARAMETER2FPROC)GetProcAddress( hmod, "cgGLGetParameter2f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameter3f = (PFNCGGLGETPARAMETER3FPROC)GetProcAddress( hmod, "cgGLGetParameter3f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameter4f = (PFNCGGLGETPARAMETER4FPROC)GetProcAddress( hmod, "cgGLGetParameter4f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameter1d = (PFNCGGLGETPARAMETER1DPROC)GetProcAddress( hmod, "cgGLGetParameter1d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameter2d = (PFNCGGLGETPARAMETER2DPROC)GetProcAddress( hmod, "cgGLGetParameter2d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameter3d = (PFNCGGLGETPARAMETER3DPROC)GetProcAddress( hmod, "cgGLGetParameter3d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameter4d = (PFNCGGLGETPARAMETER4DPROC)GetProcAddress( hmod, "cgGLGetParameter4d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameterArray1f = (PFNCGGLSETPARAMETERARRAY1FPROC)GetProcAddress( hmod, "cgGLSetParameterArray1f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameterArray2f = (PFNCGGLSETPARAMETERARRAY2FPROC)GetProcAddress( hmod, "cgGLSetParameterArray2f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameterArray3f = (PFNCGGLSETPARAMETERARRAY3FPROC)GetProcAddress( hmod, "cgGLSetParameterArray3f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameterArray4f = (PFNCGGLSETPARAMETERARRAY4FPROC)GetProcAddress( hmod, "cgGLSetParameterArray4f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameterArray1d = (PFNCGGLSETPARAMETERARRAY1DPROC)GetProcAddress( hmod, "cgGLSetParameterArray1d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameterArray2d = (PFNCGGLSETPARAMETERARRAY2DPROC)GetProcAddress( hmod, "cgGLSetParameterArray2d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameterArray3d = (PFNCGGLSETPARAMETERARRAY3DPROC)GetProcAddress( hmod, "cgGLSetParameterArray3d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameterArray4d = (PFNCGGLSETPARAMETERARRAY4DPROC)GetProcAddress( hmod, "cgGLSetParameterArray4d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameterArray1f = (PFNCGGLGETPARAMETERARRAY1FPROC)GetProcAddress( hmod, "cgGLGetParameterArray1f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameterArray2f = (PFNCGGLGETPARAMETERARRAY2FPROC)GetProcAddress( hmod, "cgGLGetParameterArray2f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameterArray3f = (PFNCGGLGETPARAMETERARRAY3FPROC)GetProcAddress( hmod, "cgGLGetParameterArray3f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameterArray4f = (PFNCGGLGETPARAMETERARRAY4FPROC)GetProcAddress( hmod, "cgGLGetParameterArray4f" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameterArray1d = (PFNCGGLGETPARAMETERARRAY1DPROC)GetProcAddress( hmod, "cgGLGetParameterArray1d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameterArray2d = (PFNCGGLGETPARAMETERARRAY2DPROC)GetProcAddress( hmod, "cgGLGetParameterArray2d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameterArray3d = (PFNCGGLGETPARAMETERARRAY3DPROC)GetProcAddress( hmod, "cgGLGetParameterArray3d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetParameterArray4d = (PFNCGGLGETPARAMETERARRAY4DPROC)GetProcAddress( hmod, "cgGLGetParameterArray4d" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetParameterPointer = (PFNCGGLSETPARAMETERPOINTERPROC)GetProcAddress( hmod, "cgGLSetParameterPointer" )))
|
||||
failed++;
|
||||
if(0 == (cgGLEnableClientState = (PFNCGGLENABLECLIENTSTATEPROC)GetProcAddress( hmod, "cgGLEnableClientState" )))
|
||||
failed++;
|
||||
if(0 == (cgGLDisableClientState = (PFNCGGLDISABLECLIENTSTATEPROC)GetProcAddress( hmod, "cgGLDisableClientState" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetMatrixParameterdr = (PFNCGGLSETMATRIXPARAMETERDRPROC)GetProcAddress( hmod, "cgGLSetMatrixParameterdr" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetMatrixParameterfr = (PFNCGGLSETMATRIXPARAMETERFRPROC)GetProcAddress( hmod, "cgGLSetMatrixParameterfr" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetMatrixParameterdc = (PFNCGGLSETMATRIXPARAMETERDCPROC)GetProcAddress( hmod, "cgGLSetMatrixParameterdc" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetMatrixParameterfc = (PFNCGGLSETMATRIXPARAMETERFCPROC)GetProcAddress( hmod, "cgGLSetMatrixParameterfc" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetMatrixParameterdr = (PFNCGGLGETMATRIXPARAMETERDRPROC)GetProcAddress( hmod, "cgGLGetMatrixParameterdr" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetMatrixParameterfr = (PFNCGGLGETMATRIXPARAMETERFRPROC)GetProcAddress( hmod, "cgGLGetMatrixParameterfr" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetMatrixParameterdc = (PFNCGGLGETMATRIXPARAMETERDCPROC)GetProcAddress( hmod, "cgGLGetMatrixParameterdc" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetMatrixParameterfc = (PFNCGGLGETMATRIXPARAMETERFCPROC)GetProcAddress( hmod, "cgGLGetMatrixParameterfc" )))
|
||||
failed++;
|
||||
if(0 == (cgGLSetStateMatrixParameter = (PFNCGGLSETSTATEMATRIXPARAMETERPROC)GetProcAddress( hmod, "cgGLSetStateMatrixParameter" )))
|
||||
failed++;
|
||||
//if(0 == (cgGLSetMatrixParameterArrayfc = (PFNCGGLSETMATRIXPARAMETERARRAYFCPROC)GetProcAddress( hmod, "cgGLSetMatrixParameterArrayfc" )))
|
||||
// failed++;
|
||||
//if(0 == (cgGLSetMatrixParameterArrayfr = (PFNCGGLSETMATRIXPARAMETERARRAYFRPROC)GetProcAddress( hmod, "cgGLSetMatrixParameterArrayfr" )))
|
||||
// failed++;
|
||||
//if(0 == (cgGLSetMatrixParameterArraydc = (PFNCGGLSETMATRIXPARAMETERARRAYDCPROC)GetProcAddress( hmod, "cgGLSetMatrixParameterArraydc" )))
|
||||
// failed++;
|
||||
//if(0 == (cgGLSetMatrixParameterArraydr = (PFNCGGLSETMATRIXPARAMETERARRAYDRPROC)GetProcAddress( hmod, "cgGLSetMatrixParameterArraydr" )))
|
||||
// failed++;
|
||||
//if(0 == (cgGLGetMatrixParameterArrayfc = (PFNCGGLGETMATRIXPARAMETERARRAYFCPROC)GetProcAddress( hmod, "cgGLGetMatrixParameterArrayfc" )))
|
||||
// failed++;
|
||||
//if(0 == (cgGLGetMatrixParameterArrayfr = (PFNCGGLGETMATRIXPARAMETERARRAYFRPROC)GetProcAddress( hmod, "cgGLGetMatrixParameterArrayfr" )))
|
||||
// failed++;
|
||||
//if(0 == (cgGLGetMatrixParameterArraydc = (PFNCGGLGETMATRIXPARAMETERARRAYDCPROC)GetProcAddress( hmod, "cgGLGetMatrixParameterArraydc" )))
|
||||
// failed++;
|
||||
//if(0 == (cgGLGetMatrixParameterArraydr = (PFNCGGLGETMATRIXPARAMETERARRAYDRPROC)GetProcAddress( hmod, "cgGLGetMatrixParameterArraydr" )))
|
||||
// failed++;
|
||||
if(0 == (cgGLSetTextureParameter = (PFNCGGLSETTEXTUREPARAMETERPROC)GetProcAddress( hmod, "cgGLSetTextureParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetTextureParameter = (PFNCGGLGETTEXTUREPARAMETERPROC)GetProcAddress( hmod, "cgGLGetTextureParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGLEnableTextureParameter = (PFNCGGLENABLETEXTUREPARAMETERPROC)GetProcAddress( hmod, "cgGLEnableTextureParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGLDisableTextureParameter = (PFNCGGLDISABLETEXTUREPARAMETERPROC)GetProcAddress( hmod, "cgGLDisableTextureParameter" )))
|
||||
failed++;
|
||||
if(0 == (cgGLGetTextureEnum = (PFNCGGLGETTEXTUREENUMPROC)GetProcAddress( hmod, "cgGLGetTextureEnum" )))
|
||||
failed++;
|
||||
|
||||
return failed == 0;
|
||||
|
||||
}
|
||||
#endif
|
||||
866
neo/renderer/cg_explicit.h
Normal file
866
neo/renderer/cg_explicit.h
Normal file
@@ -0,0 +1,866 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef CG_EXTERNAL___H
|
||||
#define CG_EXTERNAL___H
|
||||
|
||||
|
||||
|
||||
typedef int CGbool;
|
||||
typedef struct _CGcontext *CGcontext;
|
||||
typedef struct _CGprogram *CGprogram;
|
||||
typedef struct _CGparameter *CGparameter;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CG_UNKNOWN_TYPE,
|
||||
CG_STRUCT,
|
||||
CG_ARRAY,
|
||||
|
||||
CG_TYPE_START_ENUM = 1024,
|
||||
|
||||
|
||||
CG_HALF ,
|
||||
CG_HALF2 ,
|
||||
CG_HALF3 ,
|
||||
CG_HALF4 ,
|
||||
CG_HALF1x1 ,
|
||||
CG_HALF1x2 ,
|
||||
CG_HALF1x3 ,
|
||||
CG_HALF1x4 ,
|
||||
CG_HALF2x1 ,
|
||||
CG_HALF2x2 ,
|
||||
CG_HALF2x3 ,
|
||||
CG_HALF2x4 ,
|
||||
CG_HALF3x1 ,
|
||||
CG_HALF3x2 ,
|
||||
CG_HALF3x3 ,
|
||||
CG_HALF3x4 ,
|
||||
CG_HALF4x1 ,
|
||||
CG_HALF4x2 ,
|
||||
CG_HALF4x3 ,
|
||||
CG_HALF4x4 ,
|
||||
CG_FLOAT ,
|
||||
CG_FLOAT2 ,
|
||||
CG_FLOAT3 ,
|
||||
CG_FLOAT4 ,
|
||||
CG_FLOAT1x1 ,
|
||||
CG_FLOAT1x2 ,
|
||||
CG_FLOAT1x3 ,
|
||||
CG_FLOAT1x4 ,
|
||||
CG_FLOAT2x1 ,
|
||||
CG_FLOAT2x2 ,
|
||||
CG_FLOAT2x3 ,
|
||||
CG_FLOAT2x4 ,
|
||||
CG_FLOAT3x1 ,
|
||||
CG_FLOAT3x2 ,
|
||||
CG_FLOAT3x3 ,
|
||||
CG_FLOAT3x4 ,
|
||||
CG_FLOAT4x1 ,
|
||||
CG_FLOAT4x2 ,
|
||||
CG_FLOAT4x3 ,
|
||||
CG_FLOAT4x4 ,
|
||||
CG_SAMPLER1D ,
|
||||
CG_SAMPLER2D ,
|
||||
CG_SAMPLER3D ,
|
||||
CG_SAMPLERRECT ,
|
||||
CG_SAMPLERCUBE ,
|
||||
CG_FIXED ,
|
||||
CG_FIXED2 ,
|
||||
CG_FIXED3 ,
|
||||
CG_FIXED4 ,
|
||||
CG_FIXED1x1 ,
|
||||
CG_FIXED1x2 ,
|
||||
CG_FIXED1x3 ,
|
||||
CG_FIXED1x4 ,
|
||||
CG_FIXED2x1 ,
|
||||
CG_FIXED2x2 ,
|
||||
CG_FIXED2x3 ,
|
||||
CG_FIXED2x4 ,
|
||||
CG_FIXED3x1 ,
|
||||
CG_FIXED3x2 ,
|
||||
CG_FIXED3x3 ,
|
||||
CG_FIXED3x4 ,
|
||||
CG_FIXED4x1 ,
|
||||
CG_FIXED4x2 ,
|
||||
CG_FIXED4x3 ,
|
||||
CG_FIXED4x4 ,
|
||||
CG_HALF1 ,
|
||||
CG_FLOAT1 ,
|
||||
CG_FIXED1 ,
|
||||
|
||||
|
||||
} CGtype;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
||||
CG_TEXUNIT0 = 2048,
|
||||
CG_TEXUNIT1 = 2049,
|
||||
CG_TEXUNIT2 = 2050,
|
||||
CG_TEXUNIT3 = 2051,
|
||||
CG_TEXUNIT4 = 2052,
|
||||
CG_TEXUNIT5 = 2053,
|
||||
CG_TEXUNIT6 = 2054,
|
||||
CG_TEXUNIT7 = 2055,
|
||||
CG_TEXUNIT8 = 2056,
|
||||
CG_TEXUNIT9 = 2057,
|
||||
CG_TEXUNIT10 = 2058,
|
||||
CG_TEXUNIT11 = 2059,
|
||||
CG_TEXUNIT12 = 2060,
|
||||
CG_TEXUNIT13 = 2061,
|
||||
CG_TEXUNIT14 = 2062,
|
||||
CG_TEXUNIT15 = 2063,
|
||||
|
||||
CG_ATTR0 = 2113,
|
||||
CG_ATTR1 = 2114,
|
||||
CG_ATTR2 = 2115,
|
||||
CG_ATTR3 = 2116,
|
||||
CG_ATTR4 = 2117,
|
||||
CG_ATTR5 = 2118,
|
||||
CG_ATTR6 = 2119,
|
||||
CG_ATTR7 = 2120,
|
||||
CG_ATTR8 = 2121,
|
||||
CG_ATTR9 = 2122,
|
||||
CG_ATTR10 = 2123,
|
||||
CG_ATTR11 = 2124,
|
||||
CG_ATTR12 = 2125,
|
||||
CG_ATTR13 = 2126,
|
||||
CG_ATTR14 = 2127,
|
||||
CG_ATTR15 = 2128,
|
||||
|
||||
CG_C = 2178,
|
||||
|
||||
CG_TEX0 = 2179,
|
||||
CG_TEX1 = 2180,
|
||||
CG_TEX2 = 2181,
|
||||
CG_TEX3 = 2192,
|
||||
CG_TEX4 = 2193,
|
||||
CG_TEX5 = 2194,
|
||||
CG_TEX6 = 2195,
|
||||
CG_TEX7 = 2196,
|
||||
|
||||
CG_HPOS = 2243,
|
||||
CG_COL0 = 2245,
|
||||
CG_COL1 = 2246,
|
||||
CG_COL2 = 2247,
|
||||
CG_COL3 = 2248,
|
||||
CG_PSIZ = 2309,
|
||||
CG_WPOS = 2373,
|
||||
|
||||
CG_POSITION0 = 2437,
|
||||
CG_POSITION1 = 2438,
|
||||
CG_POSITION2 = 2439,
|
||||
CG_POSITION3 = 2440,
|
||||
CG_POSITION4 = 2441,
|
||||
CG_POSITION5 = 2442,
|
||||
CG_POSITION6 = 2443,
|
||||
CG_POSITION7 = 2444,
|
||||
CG_POSITION8 = 2445,
|
||||
CG_POSITION9 = 2446,
|
||||
CG_POSITION10 = 2447,
|
||||
CG_POSITION11 = 2448,
|
||||
CG_POSITION12 = 2449,
|
||||
CG_POSITION13 = 2450,
|
||||
CG_POSITION14 = 2451,
|
||||
CG_POSITION15 = 2452,
|
||||
CG_DIFFUSE0 = 2501,
|
||||
CG_TANGENT0 = 2565,
|
||||
CG_TANGENT1 = 2566,
|
||||
CG_TANGENT2 = 2567,
|
||||
CG_TANGENT3 = 2568,
|
||||
CG_TANGENT4 = 2569,
|
||||
CG_TANGENT5 = 2570,
|
||||
CG_TANGENT6 = 2571,
|
||||
CG_TANGENT7 = 2572,
|
||||
CG_TANGENT8 = 2573,
|
||||
CG_TANGENT9 = 2574,
|
||||
CG_TANGENT10 = 2575,
|
||||
CG_TANGENT11 = 2576,
|
||||
CG_TANGENT12 = 2577,
|
||||
CG_TANGENT13 = 2578,
|
||||
CG_TANGENT14 = 2579,
|
||||
CG_TANGENT15 = 2580,
|
||||
CG_SPECULAR0 = 2629,
|
||||
CG_BLENDINDICES0 = 2693,
|
||||
CG_BLENDINDICES1 = 2694,
|
||||
CG_BLENDINDICES2 = 2695,
|
||||
CG_BLENDINDICES3 = 2696,
|
||||
CG_BLENDINDICES4 = 2697,
|
||||
CG_BLENDINDICES5 = 2698,
|
||||
CG_BLENDINDICES6 = 2699,
|
||||
CG_BLENDINDICES7 = 2700,
|
||||
CG_BLENDINDICES8 = 2701,
|
||||
CG_BLENDINDICES9 = 2702,
|
||||
CG_BLENDINDICES10 = 2703,
|
||||
CG_BLENDINDICES11 = 2704,
|
||||
CG_BLENDINDICES12 = 2705,
|
||||
CG_BLENDINDICES13 = 2706,
|
||||
CG_BLENDINDICES14 = 2707,
|
||||
CG_BLENDINDICES15 = 2708,
|
||||
CG_COLOR0 = 2757,
|
||||
CG_COLOR1 = 2758,
|
||||
CG_COLOR2 = 2759,
|
||||
CG_COLOR3 = 2760,
|
||||
CG_COLOR4 = 2761,
|
||||
CG_COLOR5 = 2762,
|
||||
CG_COLOR6 = 2763,
|
||||
CG_COLOR7 = 2764,
|
||||
CG_COLOR8 = 2765,
|
||||
CG_COLOR9 = 2766,
|
||||
CG_COLOR10 = 2767,
|
||||
CG_COLOR11 = 2768,
|
||||
CG_COLOR12 = 2769,
|
||||
CG_COLOR13 = 2770,
|
||||
CG_COLOR14 = 2771,
|
||||
CG_COLOR15 = 2772,
|
||||
CG_PSIZE0 = 2821,
|
||||
CG_PSIZE1 = 2822,
|
||||
CG_PSIZE2 = 2823,
|
||||
CG_PSIZE3 = 2824,
|
||||
CG_PSIZE4 = 2825,
|
||||
CG_PSIZE5 = 2826,
|
||||
CG_PSIZE6 = 2827,
|
||||
CG_PSIZE7 = 2828,
|
||||
CG_PSIZE8 = 2829,
|
||||
CG_PSIZE9 = 2830,
|
||||
CG_PSIZE10 = 2831,
|
||||
CG_PSIZE11 = 2832,
|
||||
CG_PSIZE12 = 2833,
|
||||
CG_PSIZE13 = 2834,
|
||||
CG_PSIZE14 = 2835,
|
||||
CG_PSIZE15 = 2836,
|
||||
CG_BINORMAL0 = 2885,
|
||||
CG_BINORMAL1 = 2886,
|
||||
CG_BINORMAL2 = 2887,
|
||||
CG_BINORMAL3 = 2888,
|
||||
CG_BINORMAL4 = 2889,
|
||||
CG_BINORMAL5 = 2890,
|
||||
CG_BINORMAL6 = 2891,
|
||||
CG_BINORMAL7 = 2892,
|
||||
CG_BINORMAL8 = 2893,
|
||||
CG_BINORMAL9 = 2894,
|
||||
CG_BINORMAL10 = 2895,
|
||||
CG_BINORMAL11 = 2896,
|
||||
CG_BINORMAL12 = 2897,
|
||||
CG_BINORMAL13 = 2898,
|
||||
CG_BINORMAL14 = 2899,
|
||||
CG_BINORMAL15 = 2900,
|
||||
CG_FOG0 = 2917,
|
||||
CG_FOG1 = 2918,
|
||||
CG_FOG2 = 2919,
|
||||
CG_FOG3 = 2920,
|
||||
CG_FOG4 = 2921,
|
||||
CG_FOG5 = 2922,
|
||||
CG_FOG6 = 2923,
|
||||
CG_FOG7 = 2924,
|
||||
CG_FOG8 = 2925,
|
||||
CG_FOG9 = 2926,
|
||||
CG_FOG10 = 2927,
|
||||
CG_FOG11 = 2928,
|
||||
CG_FOG12 = 2929,
|
||||
CG_FOG13 = 2930,
|
||||
CG_FOG14 = 2931,
|
||||
CG_FOG15 = 2932,
|
||||
CG_DEPTH0 = 2933,
|
||||
CG_DEPTH1 = 2934,
|
||||
CG_DEPTH2 = 2935,
|
||||
CG_DEPTH3 = 2936,
|
||||
CG_DEPTH4 = 2937,
|
||||
CG_DEPTH5 = 2938,
|
||||
CG_DEPTH6 = 2939,
|
||||
CG_DEPTH7 = 2940,
|
||||
CG_DEPTH8 = 2941,
|
||||
CG_DEPTH9 = 29542,
|
||||
CG_DEPTH10 = 2943,
|
||||
CG_DEPTH11 = 2944,
|
||||
CG_DEPTH12 = 2945,
|
||||
CG_DEPTH13 = 2946,
|
||||
CG_DEPTH14 = 2947,
|
||||
CG_DEPTH15 = 2948,
|
||||
CG_SAMPLE0 = 2949,
|
||||
CG_SAMPLE1 = 2950,
|
||||
CG_SAMPLE2 = 2951,
|
||||
CG_SAMPLE3 = 2952,
|
||||
CG_SAMPLE4 = 2953,
|
||||
CG_SAMPLE5 = 2954,
|
||||
CG_SAMPLE6 = 2955,
|
||||
CG_SAMPLE7 = 2956,
|
||||
CG_SAMPLE8 = 2957,
|
||||
CG_SAMPLE9 = 2958,
|
||||
CG_SAMPLE10 = 2959,
|
||||
CG_SAMPLE11 = 2960,
|
||||
CG_SAMPLE12 = 2961,
|
||||
CG_SAMPLE13 = 2962,
|
||||
CG_SAMPLE14 = 2963,
|
||||
CG_SAMPLE15 = 2964,
|
||||
CG_BLENDWEIGHT0 = 3028,
|
||||
CG_BLENDWEIGHT1 = 3029,
|
||||
CG_BLENDWEIGHT2 = 3030,
|
||||
CG_BLENDWEIGHT3 = 3031,
|
||||
CG_BLENDWEIGHT4 = 3032,
|
||||
CG_BLENDWEIGHT5 = 3033,
|
||||
CG_BLENDWEIGHT6 = 3034,
|
||||
CG_BLENDWEIGHT7 = 3035,
|
||||
CG_BLENDWEIGHT8 = 3036,
|
||||
CG_BLENDWEIGHT9 = 3037,
|
||||
CG_BLENDWEIGHT10 = 3038,
|
||||
CG_BLENDWEIGHT11 = 3039,
|
||||
CG_BLENDWEIGHT12 = 3040,
|
||||
CG_BLENDWEIGHT13 = 3041,
|
||||
CG_BLENDWEIGHT14 = 3042,
|
||||
CG_BLENDWEIGHT15 = 3043,
|
||||
CG_NORMAL0 = 3092,
|
||||
CG_NORMAL1 = 3093,
|
||||
CG_NORMAL2 = 3094,
|
||||
CG_NORMAL3 = 3095,
|
||||
CG_NORMAL4 = 3096,
|
||||
CG_NORMAL5 = 3097,
|
||||
CG_NORMAL6 = 3098,
|
||||
CG_NORMAL7 = 3099,
|
||||
CG_NORMAL8 = 3100,
|
||||
CG_NORMAL9 = 3101,
|
||||
CG_NORMAL10 = 3102,
|
||||
CG_NORMAL11 = 3103,
|
||||
CG_NORMAL12 = 3104,
|
||||
CG_NORMAL13 = 3105,
|
||||
CG_NORMAL14 = 3106,
|
||||
CG_NORMAL15 = 3107,
|
||||
CG_FOGCOORD = 3156,
|
||||
CG_TEXCOORD0 = 3220,
|
||||
CG_TEXCOORD1 = 3221,
|
||||
CG_TEXCOORD2 = 3222,
|
||||
CG_TEXCOORD3 = 3223,
|
||||
CG_TEXCOORD4 = 3224,
|
||||
CG_TEXCOORD5 = 3225,
|
||||
CG_TEXCOORD6 = 3226,
|
||||
CG_TEXCOORD7 = 3227,
|
||||
CG_TEXCOORD8 = 3228,
|
||||
CG_TEXCOORD9 = 3229,
|
||||
CG_TEXCOORD10 = 3230,
|
||||
CG_TEXCOORD11 = 3231,
|
||||
CG_TEXCOORD12 = 3232,
|
||||
CG_TEXCOORD13 = 3233,
|
||||
CG_TEXCOORD14 = 3234,
|
||||
CG_TEXCOORD15 = 3235,
|
||||
CG_COMBINER_CONST0 = 3284,
|
||||
CG_COMBINER_CONST1 = 3285,
|
||||
CG_COMBINER_STAGE_CONST0 = 3286,
|
||||
CG_COMBINER_STAGE_CONST1 = 3287,
|
||||
CG_OFFSET_TEXTURE_MATRIX = 3288,
|
||||
CG_OFFSET_TEXTURE_SCALE = 3289,
|
||||
CG_OFFSET_TEXTURE_BIAS = 3290,
|
||||
CG_CONST_EYE = 3291,
|
||||
CG_TESSFACTOR = 3255,
|
||||
|
||||
|
||||
CG_UNDEFINED,
|
||||
|
||||
} CGresource;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CG_PROFILE_START = 6144,
|
||||
CG_PROFILE_UNKNOWN,
|
||||
|
||||
CG_PROFILE_VP20 = 6146,
|
||||
CG_PROFILE_FP20 = 6147,
|
||||
CG_PROFILE_VP30 = 6148,
|
||||
CG_PROFILE_FP30 = 6149,
|
||||
CG_PROFILE_ARBVP1 = 6150,
|
||||
CG_PROFILE_ARBFP1 = 7000,
|
||||
|
||||
|
||||
CG_PROFILE_VS_1_1 = 6153,
|
||||
CG_PROFILE_VS_2_0 = 6154,
|
||||
CG_PROFILE_VS_2_X = 6155,
|
||||
|
||||
CG_PROFILE_PS_1_1 = 6159,
|
||||
CG_PROFILE_PS_1_2 = 6160,
|
||||
CG_PROFILE_PS_1_3 = 6161,
|
||||
CG_PROFILE_PS_2_0 = 6162,
|
||||
CG_PROFILE_PS_2_X = 6163,
|
||||
|
||||
CG_PROFILE_MAX,
|
||||
} CGprofile;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
||||
|
||||
CG_NO_ERROR = 0,
|
||||
CG_COMPILER_ERROR = 1,
|
||||
CG_INVALID_PARAMETER_ERROR = 2,
|
||||
CG_INVALID_PROFILE_ERROR = 3,
|
||||
CG_PROGRAM_LOAD_ERROR = 4,
|
||||
CG_PROGRAM_BIND_ERROR = 5,
|
||||
CG_PROGRAM_NOT_LOADED_ERROR = 6,
|
||||
CG_UNSUPPORTED_GL_EXTENSION_ERROR = 7,
|
||||
CG_INVALID_VALUE_TYPE_ERROR = 8,
|
||||
CG_NOT_MATRIX_PARAM_ERROR = 9,
|
||||
CG_INVALID_ENUMERANT_ERROR = 10,
|
||||
CG_NOT_4x4_MATRIX_ERROR = 11,
|
||||
CG_FILE_READ_ERROR = 12,
|
||||
CG_FILE_WRITE_ERROR = 13,
|
||||
CG_NVPARSE_ERROR = 14,
|
||||
CG_MEMORY_ALLOC_ERROR = 15,
|
||||
CG_INVALID_CONTEXT_HANDLE_ERROR = 16,
|
||||
CG_INVALID_PROGRAM_HANDLE_ERROR = 17,
|
||||
CG_INVALID_PARAM_HANDLE_ERROR = 18,
|
||||
CG_UNKNOWN_PROFILE_ERROR = 19,
|
||||
CG_VAR_ARG_ERROR = 20,
|
||||
CG_INVALID_DIMENSION_ERROR = 21,
|
||||
CG_ARRAY_PARAM_ERROR = 22,
|
||||
CG_OUT_OF_ARRAY_BOUNDS_ERROR = 23,
|
||||
} CGerror;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CG_UNKNOWN = 4096,
|
||||
CG_IN,
|
||||
CG_OUT,
|
||||
CG_INOUT,
|
||||
CG_MIXED,
|
||||
CG_VARYING,
|
||||
CG_UNIFORM,
|
||||
CG_CONSTANT,
|
||||
CG_PROGRAM_SOURCE,
|
||||
CG_PROGRAM_ENTRY,
|
||||
CG_COMPILED_PROGRAM,
|
||||
CG_PROGRAM_PROFILE,
|
||||
|
||||
CG_GLOBAL,
|
||||
CG_PROGRAM,
|
||||
|
||||
CG_DEFAULT,
|
||||
CG_ERROR,
|
||||
|
||||
CG_SOURCE,
|
||||
CG_OBJECT,
|
||||
|
||||
} CGenum;
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
typedef void (*CGerrorCallbackFunc)(void);
|
||||
|
||||
|
||||
|
||||
typedef CGcontext (*PFNCGCREATECONTEXTPROC)(void);
|
||||
typedef void (*PFNCGDESTROYCONTEXTPROC)(CGcontext ctx);
|
||||
typedef CGbool (*PFNCGISCONTEXTPROC)(CGcontext ctx);
|
||||
typedef const char * (*PFNCGGETLASTLISTINGPROC)(CGcontext ctx);
|
||||
typedef CGprogram (*PFNCGCREATEPROGRAMPROC)(CGcontext ctx,
|
||||
CGenum program_type,
|
||||
const char *program,
|
||||
CGprofile profile,
|
||||
const char *entry,
|
||||
const char **args);
|
||||
typedef CGprogram (*PFNCGCREATEPROGRAMFROMFILEPROC)(CGcontext ctx,
|
||||
CGenum program_type,
|
||||
const char *program_file,
|
||||
CGprofile profile,
|
||||
const char *entry,
|
||||
const char **args);
|
||||
typedef CGprogram (*PFNCGCOPYPROGRAMPROC)(CGprogram program);
|
||||
typedef void (*PFNCGDESTROYPROGRAMPROC)(CGprogram program);
|
||||
typedef CGprogram (*PFNCGGETFIRSTPROGRAMPROC)(CGcontext ctx);
|
||||
typedef CGprogram (*PFNCGGETNEXTPROGRAMPROC)(CGprogram current);
|
||||
typedef CGcontext (*PFNCGGETPROGRAMCONTEXTPROC)(CGprogram prog);
|
||||
typedef CGbool (*PFNCGISPROGRAMPROC)(CGprogram program);
|
||||
typedef void (*PFNCGCOMPILEPROGRAMPROC)(CGprogram program);
|
||||
typedef CGbool (*PFNCGISPROGRAMCOMPILEDPROC)(CGprogram program);
|
||||
typedef const char * (*PFNCGGETPROGRAMSTRINGPROC)(CGprogram prog, CGenum pname);
|
||||
typedef CGprofile (*PFNCGGETPROGRAMPROFILEPROC)(CGprogram prog);
|
||||
typedef CGparameter (*PFNCGGETNAMEDPARAMETERPROC)(CGprogram prog, const char *name);
|
||||
typedef CGparameter (*PFNCGGETFIRSTPARAMETERPROC)(CGprogram prog, CGenum name_space);
|
||||
typedef CGparameter (*PFNCGGETNEXTPARAMETERPROC)(CGparameter current);
|
||||
typedef CGparameter (*PFNCGGETFIRSTLEAFPARAMETERPROC)(CGprogram prog, CGenum name_space);
|
||||
typedef CGparameter (*PFNCGGETNEXTLEAFPARAMETERPROC)(CGparameter current);
|
||||
typedef CGparameter (*PFNCGGETFIRSTSTRUCTPARAMETERPROC)(CGparameter param);
|
||||
typedef CGparameter (*PFNCGGETFIRSTDEPENDENTPARAMETERPROC)(CGparameter param);
|
||||
typedef CGparameter (*PFNCGGETARRAYPARAMETERPROC)(CGparameter aparam, int index);
|
||||
typedef int (*PFNCGGETARRAYDIMENSIONPROC)(CGparameter param);
|
||||
typedef int (*PFNCGGETARRAYSIZEPROC)(CGparameter param, int dimension);
|
||||
typedef CGprogram (*PFNCGGETPARAMETERPROGRAMPROC)(CGparameter prog);
|
||||
typedef CGbool (*PFNCGISPARAMETERPROC)(CGparameter param);
|
||||
typedef const char * (*PFNCGGETPARAMETERNAMEPROC)(CGparameter param);
|
||||
typedef CGtype (*PFNCGGETPARAMETERTYPEPROC)(CGparameter param);
|
||||
typedef const char * (*PFNCGGETPARAMETERSEMANTICPROC)(CGparameter param);
|
||||
typedef CGresource (*PFNCGGETPARAMETERRESOURCEPROC)(CGparameter param);
|
||||
typedef CGresource (*PFNCGGETPARAMETERBASERESOURCEPROC)(CGparameter param);
|
||||
typedef unsigned long (*PFNCGGETPARAMETERRESOURCEINDEXPROC)(CGparameter param);
|
||||
typedef CGenum (*PFNCGGETPARAMETERVARIABILITYPROC)(CGparameter param);
|
||||
typedef CGenum (*PFNCGGETPARAMETERDIRECTIONPROC)(CGparameter param);
|
||||
typedef CGbool (*PFNCGISPARAMETERREFERENCEDPROC)(CGparameter param);
|
||||
typedef void (*PFNCGGETPARAMETERVALUESPROC)(CGparameter param,
|
||||
CGenum value_type,
|
||||
int *nvalues);
|
||||
typedef const char * (*PFNCGGETTYPESTRINGPROC)(CGtype type);
|
||||
typedef CGtype (*PFNCGGETTYPEPROC)(const char *type_string);
|
||||
typedef const char * (*PFNCGGETRESOURCESTRINGPROC)(CGresource resource);
|
||||
typedef CGresource (*PFNCGGETRESOURCEPROC)(const char *resource_string);
|
||||
typedef const char * (*PFNCGGETPROFILESTRINGPROC)(CGprofile profile);
|
||||
typedef CGprofile (*PFNCGGETPROFILEPROC)(const char *profile_string);
|
||||
typedef CGerror (*PFNCGGETERRORPROC)(void);
|
||||
typedef const char * (*PFNCGGETERRORSTRINGPROC)(CGerror error);
|
||||
typedef void (*PFNCGSETERRORCALLBACKPROC)(CGerrorCallbackFunc func);
|
||||
typedef CGerrorCallbackFunc (*PFNCGGETERRORCALLBACKPROC)(void);
|
||||
|
||||
|
||||
extern PFNCGCREATECONTEXTPROC cgCreateContext;
|
||||
extern PFNCGDESTROYCONTEXTPROC cgDestroyContext;
|
||||
extern PFNCGISCONTEXTPROC cgIsContext;
|
||||
extern PFNCGGETLASTLISTINGPROC cgGetLastListing;
|
||||
extern PFNCGCREATEPROGRAMPROC cgCreateProgram;
|
||||
extern PFNCGCREATEPROGRAMFROMFILEPROC cgCreateProgramFromFile;
|
||||
extern PFNCGCOPYPROGRAMPROC cgCopyProgram;
|
||||
extern PFNCGDESTROYPROGRAMPROC cgDestroyProgram;
|
||||
extern PFNCGGETFIRSTPROGRAMPROC cgGetFirstProgram;
|
||||
extern PFNCGGETNEXTPROGRAMPROC cgGetNextProgram;
|
||||
extern PFNCGGETPROGRAMCONTEXTPROC cgGetProgramContext;
|
||||
extern PFNCGISPROGRAMPROC cgIsProgram;
|
||||
extern PFNCGCOMPILEPROGRAMPROC cgCompileProgram;
|
||||
extern PFNCGISPROGRAMCOMPILEDPROC cgIsProgramCompiled;
|
||||
extern PFNCGGETPROGRAMSTRINGPROC cgGetProgramString;
|
||||
extern PFNCGGETPROGRAMPROFILEPROC cgGetProgramProfile;
|
||||
extern PFNCGGETNAMEDPARAMETERPROC cgGetNamedParameter;
|
||||
extern PFNCGGETFIRSTPARAMETERPROC cgGetFirstParameter;
|
||||
extern PFNCGGETNEXTPARAMETERPROC cgGetNextParameter;
|
||||
extern PFNCGGETFIRSTLEAFPARAMETERPROC cgGetFirstLeafParameter;
|
||||
extern PFNCGGETNEXTLEAFPARAMETERPROC cgGetNextLeafParameter;
|
||||
extern PFNCGGETFIRSTSTRUCTPARAMETERPROC cgGetFirstStructParameter;
|
||||
extern PFNCGGETFIRSTDEPENDENTPARAMETERPROC cgGetFirstDependentParameter;
|
||||
extern PFNCGGETARRAYPARAMETERPROC cgGetArrayParameter;
|
||||
extern PFNCGGETARRAYDIMENSIONPROC cgGetArrayDimension;
|
||||
extern PFNCGGETARRAYSIZEPROC cgGetArraySize;
|
||||
extern PFNCGGETPARAMETERPROGRAMPROC cgGetParameterProgram;
|
||||
extern PFNCGISPARAMETERPROC cgIsParameter;
|
||||
extern PFNCGGETPARAMETERNAMEPROC cgGetParameterName;
|
||||
extern PFNCGGETPARAMETERTYPEPROC cgGetParameterType;
|
||||
extern PFNCGGETPARAMETERSEMANTICPROC cgGetParameterSemantic;
|
||||
extern PFNCGGETPARAMETERRESOURCEPROC cgGetParameterResource;
|
||||
extern PFNCGGETPARAMETERBASERESOURCEPROC cgGetParameterBaseResource;
|
||||
extern PFNCGGETPARAMETERRESOURCEINDEXPROC cgGetParameterResourceIndex;
|
||||
extern PFNCGGETPARAMETERVARIABILITYPROC cgGetParameterVariability;
|
||||
extern PFNCGGETPARAMETERDIRECTIONPROC cgGetParameterDirection;
|
||||
extern PFNCGISPARAMETERREFERENCEDPROC cgIsParameterReferenced;
|
||||
extern PFNCGGETPARAMETERVALUESPROC cgGetParameterValues;
|
||||
extern PFNCGGETTYPESTRINGPROC cgGetTypeString;
|
||||
extern PFNCGGETTYPEPROC cgGetType;
|
||||
extern PFNCGGETRESOURCESTRINGPROC cgGetResourceString;
|
||||
extern PFNCGGETRESOURCEPROC cgGetResource;
|
||||
extern PFNCGGETPROFILESTRINGPROC cgGetProfileString;
|
||||
extern PFNCGGETPROFILEPROC cgGetProfile;
|
||||
extern PFNCGGETERRORPROC cgGetError;
|
||||
extern PFNCGGETERRORSTRINGPROC cgGetErrorString;
|
||||
extern PFNCGSETERRORCALLBACKPROC cgSetErrorCallback;
|
||||
extern PFNCGGETERRORCALLBACKPROC cgGetErrorCallback;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CG_GL_MATRIX_IDENTITY = 0,
|
||||
CG_GL_MATRIX_TRANSPOSE = 1,
|
||||
CG_GL_MATRIX_INVERSE = 2,
|
||||
CG_GL_MATRIX_INVERSE_TRANSPOSE = 3,
|
||||
|
||||
CG_GL_MODELVIEW_MATRIX,
|
||||
CG_GL_PROJECTION_MATRIX,
|
||||
CG_GL_TEXTURE_MATRIX,
|
||||
CG_GL_MODELVIEW_PROJECTION_MATRIX,
|
||||
|
||||
CG_GL_VERTEX,
|
||||
CG_GL_FRAGMENT,
|
||||
|
||||
} CGGLenum;
|
||||
|
||||
|
||||
|
||||
|
||||
typedef CGbool (*PFNCGGLISPROFILESUPPORTEDPROC)(CGprofile profile);
|
||||
typedef void (*PFNCGGLENABLEPROFILEPROC)(CGprofile profile);
|
||||
typedef void (*PFNCGGLDISABLEPROFILEPROC)(CGprofile profile);
|
||||
typedef CGprofile (*PFNCGGLGETLATESTPROFILEPROC)(CGGLenum profile_type);
|
||||
typedef void (*PFNCGGLSETOPTIMALOPTIONSPROC)(CGprofile profile);
|
||||
typedef void (*PFNCGGLLOADPROGRAMPROC)(CGprogram program);
|
||||
typedef void (*PFNCGGLBINDPROGRAMPROC)(CGprogram program);
|
||||
typedef void (*PFNCGGLSETPARAMETER1FPROC)(CGparameter param,
|
||||
float x);
|
||||
typedef void (*PFNCGGLSETPARAMETER2FPROC)(CGparameter param,
|
||||
float x,
|
||||
float y);
|
||||
typedef void (*PFNCGGLSETPARAMETER3FPROC)(CGparameter param,
|
||||
float x,
|
||||
float y,
|
||||
float z);
|
||||
typedef void (*PFNCGGLSETPARAMETER4FPROC)(CGparameter param,
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
float w);
|
||||
typedef void (*PFNCGGLSETPARAMETER1FVPROC)(CGparameter param, const float *v);
|
||||
typedef void (*PFNCGGLSETPARAMETER2FVPROC)(CGparameter param, const float *v);
|
||||
typedef void (*PFNCGGLSETPARAMETER3FVPROC)(CGparameter param, const float *v);
|
||||
typedef void (*PFNCGGLSETPARAMETER4FVPROC)(CGparameter param, const float *v);
|
||||
typedef void (*PFNCGGLSETPARAMETER1DPROC)(CGparameter param,
|
||||
double x);
|
||||
typedef void (*PFNCGGLSETPARAMETER2DPROC)(CGparameter param,
|
||||
double x,
|
||||
double y);
|
||||
typedef void (*PFNCGGLSETPARAMETER3DPROC)(CGparameter param,
|
||||
double x,
|
||||
double y,
|
||||
double z);
|
||||
typedef void (*PFNCGGLSETPARAMETER4DPROC)(CGparameter param,
|
||||
double x,
|
||||
double y,
|
||||
double z,
|
||||
double w);
|
||||
typedef void (*PFNCGGLSETPARAMETER1DVPROC)(CGparameter param, const double *v);
|
||||
typedef void (*PFNCGGLSETPARAMETER2DVPROC)(CGparameter param, const double *v);
|
||||
typedef void (*PFNCGGLSETPARAMETER3DVPROC)(CGparameter param, const double *v);
|
||||
typedef void (*PFNCGGLSETPARAMETER4DVPROC)(CGparameter param, const double *v);
|
||||
typedef void (*PFNCGGLSETPARAMETER4DVPROC)(CGparameter param, const double *v);
|
||||
typedef void (*PFNCGGLGETPARAMETER1FPROC)(CGparameter param, float *v);
|
||||
typedef void (*PFNCGGLGETPARAMETER2FPROC)(CGparameter param, float *v);
|
||||
typedef void (*PFNCGGLGETPARAMETER3FPROC)(CGparameter param, float *v);
|
||||
typedef void (*PFNCGGLGETPARAMETER4FPROC)(CGparameter param, float *v);
|
||||
typedef void (*PFNCGGLGETPARAMETER1DPROC)(CGparameter param, double *v);
|
||||
typedef void (*PFNCGGLGETPARAMETER2DPROC)(CGparameter param, double *v);
|
||||
typedef void (*PFNCGGLGETPARAMETER3DPROC)(CGparameter param, double *v);
|
||||
typedef void (*PFNCGGLGETPARAMETER4DPROC)(CGparameter param, double *v);
|
||||
typedef void (*PFNCGGLSETPARAMETERARRAY1FPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const float *v);
|
||||
typedef void (*PFNCGGLSETPARAMETERARRAY2FPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const float *v);
|
||||
typedef void (*PFNCGGLSETPARAMETERARRAY3FPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const float *v);
|
||||
typedef void (*PFNCGGLSETPARAMETERARRAY4FPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const float *v);
|
||||
typedef void (*PFNCGGLSETPARAMETERARRAY1DPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const double *v);
|
||||
typedef void (*PFNCGGLSETPARAMETERARRAY2DPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const double *v);
|
||||
typedef void (*PFNCGGLSETPARAMETERARRAY3DPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const double *v);
|
||||
typedef void (*PFNCGGLSETPARAMETERARRAY4DPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const double *v);
|
||||
typedef void (*PFNCGGLGETPARAMETERARRAY1FPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
float *v);
|
||||
typedef void (*PFNCGGLGETPARAMETERARRAY2FPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
float *v);
|
||||
typedef void (*PFNCGGLGETPARAMETERARRAY3FPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
float *v);
|
||||
typedef void (*PFNCGGLGETPARAMETERARRAY4FPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
float *v);
|
||||
typedef void (*PFNCGGLGETPARAMETERARRAY1DPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
double *v);
|
||||
typedef void (*PFNCGGLGETPARAMETERARRAY2DPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
double *v);
|
||||
typedef void (*PFNCGGLGETPARAMETERARRAY3DPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
double *v);
|
||||
typedef void (*PFNCGGLGETPARAMETERARRAY4DPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
double *v);
|
||||
typedef void (*PFNCGGLSETPARAMETERPOINTERPROC)(CGparameter param,
|
||||
GLint fsize,
|
||||
GLenum type,
|
||||
GLsizei stride,
|
||||
GLvoid *pointer);
|
||||
typedef void (*PFNCGGLENABLECLIENTSTATEPROC)(CGparameter param);
|
||||
typedef void (*PFNCGGLDISABLECLIENTSTATEPROC)(CGparameter param);
|
||||
typedef void (*PFNCGGLSETMATRIXPARAMETERDRPROC)(CGparameter param, const double *matrix);
|
||||
typedef void (*PFNCGGLSETMATRIXPARAMETERFRPROC)(CGparameter param, const float *matrix);
|
||||
typedef void (*PFNCGGLSETMATRIXPARAMETERDCPROC)(CGparameter param, const double *matrix);
|
||||
typedef void (*PFNCGGLSETMATRIXPARAMETERFCPROC)(CGparameter param, const float *matrix);
|
||||
typedef void (*PFNCGGLGETMATRIXPARAMETERDRPROC)(CGparameter param, double *matrix);
|
||||
typedef void (*PFNCGGLGETMATRIXPARAMETERFRPROC)(CGparameter param, float *matrix);
|
||||
typedef void (*PFNCGGLGETMATRIXPARAMETERDCPROC)(CGparameter param, double *matrix);
|
||||
typedef void (*PFNCGGLGETMATRIXPARAMETERFCPROC)(CGparameter param, float *matrix);
|
||||
typedef void (*PFNCGGLSETSTATEMATRIXPARAMETERPROC)(CGparameter param,
|
||||
GLenum matrix,
|
||||
GLenum transform);
|
||||
typedef void (*PFNCGGLSETMATRIXPARAMETERARRAYFCPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const float *matrices);
|
||||
typedef void (*PFNCGGLSETMATRIXPARAMETERARRAYFRPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const float *matrices);
|
||||
typedef void (*PFNCGGLSETMATRIXPARAMETERARRAYDCPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const double *matrices);
|
||||
typedef void (*PFNCGGLSETMATRIXPARAMETERARRAYDRPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
const double *matrices);
|
||||
typedef void (*PFNCGGLGETMATRIXPARAMETERARRAYFCPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
float *matrices);
|
||||
typedef void (*PFNCGGLGETMATRIXPARAMETERARRAYFRPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
float *matrices);
|
||||
typedef void (*PFNCGGLGETMATRIXPARAMETERARRAYDCPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
double *matrices);
|
||||
typedef void (*PFNCGGLGETMATRIXPARAMETERARRAYDRPROC)(CGparameter param,
|
||||
long offset,
|
||||
long nelements,
|
||||
double *matrices);
|
||||
typedef void (*PFNCGGLSETTEXTUREPARAMETERPROC)(CGparameter param, GLuint texobj);
|
||||
typedef GLuint (*PFNCGGLGETTEXTUREPARAMETERPROC)(CGparameter param);
|
||||
typedef void (*PFNCGGLENABLETEXTUREPARAMETERPROC)(CGparameter param);
|
||||
typedef void (*PFNCGGLDISABLETEXTUREPARAMETERPROC)(CGparameter param);
|
||||
typedef GLenum (*PFNCGGLGETTEXTUREENUMPROC)(CGparameter param);
|
||||
|
||||
|
||||
extern PFNCGGLISPROFILESUPPORTEDPROC cgGLIsProfileSupported;
|
||||
extern PFNCGGLENABLEPROFILEPROC cgGLEnableProfile;
|
||||
extern PFNCGGLDISABLEPROFILEPROC cgGLDisableProfile;
|
||||
extern PFNCGGLGETLATESTPROFILEPROC cgGLGetLatestProfile;
|
||||
extern PFNCGGLSETOPTIMALOPTIONSPROC cgGLSetOptimalOptions;
|
||||
extern PFNCGGLLOADPROGRAMPROC cgGLLoadProgram;
|
||||
extern PFNCGGLBINDPROGRAMPROC cgGLBindProgram;
|
||||
extern PFNCGGLSETPARAMETER1FPROC cgGLSetParameter1f;
|
||||
extern PFNCGGLSETPARAMETER2FPROC cgGLSetParameter2f;
|
||||
extern PFNCGGLSETPARAMETER3FPROC cgGLSetParameter3f;
|
||||
extern PFNCGGLSETPARAMETER4FPROC cgGLSetParameter4f;
|
||||
extern PFNCGGLSETPARAMETER1FVPROC cgGLSetParameter1fv;
|
||||
extern PFNCGGLSETPARAMETER2FVPROC cgGLSetParameter2fv;
|
||||
extern PFNCGGLSETPARAMETER3FVPROC cgGLSetParameter3fv;
|
||||
extern PFNCGGLSETPARAMETER4FVPROC cgGLSetParameter4fv;
|
||||
extern PFNCGGLSETPARAMETER1DPROC cgGLSetParameter1d;
|
||||
extern PFNCGGLSETPARAMETER2DPROC cgGLSetParameter2d;
|
||||
extern PFNCGGLSETPARAMETER3DPROC cgGLSetParameter3d;
|
||||
extern PFNCGGLSETPARAMETER4DPROC cgGLSetParameter4d;
|
||||
extern PFNCGGLSETPARAMETER1DVPROC cgGLSetParameter1dv;
|
||||
extern PFNCGGLSETPARAMETER2DVPROC cgGLSetParameter2dv;
|
||||
extern PFNCGGLSETPARAMETER3DVPROC cgGLSetParameter3dv;
|
||||
extern PFNCGGLSETPARAMETER4DVPROC cgGLSetParameter4dv;
|
||||
extern PFNCGGLGETPARAMETER1FPROC cgGLGetParameter1f;
|
||||
extern PFNCGGLGETPARAMETER2FPROC cgGLGetParameter2f;
|
||||
extern PFNCGGLGETPARAMETER3FPROC cgGLGetParameter3f;
|
||||
extern PFNCGGLGETPARAMETER4FPROC cgGLGetParameter4f;
|
||||
extern PFNCGGLGETPARAMETER1DPROC cgGLGetParameter1d;
|
||||
extern PFNCGGLGETPARAMETER2DPROC cgGLGetParameter2d;
|
||||
extern PFNCGGLGETPARAMETER3DPROC cgGLGetParameter3d;
|
||||
extern PFNCGGLGETPARAMETER4DPROC cgGLGetParameter4d;
|
||||
extern PFNCGGLSETPARAMETERARRAY1FPROC cgGLSetParameterArray1f;
|
||||
extern PFNCGGLSETPARAMETERARRAY2FPROC cgGLSetParameterArray2f;
|
||||
extern PFNCGGLSETPARAMETERARRAY3FPROC cgGLSetParameterArray3f;
|
||||
extern PFNCGGLSETPARAMETERARRAY4FPROC cgGLSetParameterArray4f;
|
||||
extern PFNCGGLSETPARAMETERARRAY1DPROC cgGLSetParameterArray1d;
|
||||
extern PFNCGGLSETPARAMETERARRAY2DPROC cgGLSetParameterArray2d;
|
||||
extern PFNCGGLSETPARAMETERARRAY3DPROC cgGLSetParameterArray3d;
|
||||
extern PFNCGGLSETPARAMETERARRAY4DPROC cgGLSetParameterArray4d;
|
||||
extern PFNCGGLGETPARAMETERARRAY1FPROC cgGLGetParameterArray1f;
|
||||
extern PFNCGGLGETPARAMETERARRAY2FPROC cgGLGetParameterArray2f;
|
||||
extern PFNCGGLGETPARAMETERARRAY3FPROC cgGLGetParameterArray3f;
|
||||
extern PFNCGGLGETPARAMETERARRAY4FPROC cgGLGetParameterArray4f;
|
||||
extern PFNCGGLGETPARAMETERARRAY1DPROC cgGLGetParameterArray1d;
|
||||
extern PFNCGGLGETPARAMETERARRAY2DPROC cgGLGetParameterArray2d;
|
||||
extern PFNCGGLGETPARAMETERARRAY3DPROC cgGLGetParameterArray3d;
|
||||
extern PFNCGGLGETPARAMETERARRAY4DPROC cgGLGetParameterArray4d;
|
||||
extern PFNCGGLSETPARAMETERPOINTERPROC cgGLSetParameterPointer;
|
||||
extern PFNCGGLENABLECLIENTSTATEPROC cgGLEnableClientState;
|
||||
extern PFNCGGLDISABLECLIENTSTATEPROC cgGLDisableClientState;
|
||||
extern PFNCGGLSETMATRIXPARAMETERDRPROC cgGLSetMatrixParameterdr;
|
||||
extern PFNCGGLSETMATRIXPARAMETERFRPROC cgGLSetMatrixParameterfr;
|
||||
extern PFNCGGLSETMATRIXPARAMETERDCPROC cgGLSetMatrixParameterdc;
|
||||
extern PFNCGGLSETMATRIXPARAMETERFCPROC cgGLSetMatrixParameterfc;
|
||||
extern PFNCGGLGETMATRIXPARAMETERDRPROC cgGLGetMatrixParameterdr;
|
||||
extern PFNCGGLGETMATRIXPARAMETERFRPROC cgGLGetMatrixParameterfr;
|
||||
extern PFNCGGLGETMATRIXPARAMETERDCPROC cgGLGetMatrixParameterdc;
|
||||
extern PFNCGGLGETMATRIXPARAMETERFCPROC cgGLGetMatrixParameterfc;
|
||||
extern PFNCGGLSETSTATEMATRIXPARAMETERPROC cgGLSetStateMatrixParameter;
|
||||
extern PFNCGGLSETMATRIXPARAMETERARRAYFCPROC cgGLSetMatrixParameterArrayfc;
|
||||
extern PFNCGGLSETMATRIXPARAMETERARRAYFRPROC cgGLSetMatrixParameterArrayfr;
|
||||
extern PFNCGGLSETMATRIXPARAMETERARRAYDCPROC cgGLSetMatrixParameterArraydc;
|
||||
extern PFNCGGLSETMATRIXPARAMETERARRAYDRPROC cgGLSetMatrixParameterArraydr;
|
||||
extern PFNCGGLGETMATRIXPARAMETERARRAYFCPROC cgGLGetMatrixParameterArrayfc;
|
||||
extern PFNCGGLGETMATRIXPARAMETERARRAYFRPROC cgGLGetMatrixParameterArrayfr;
|
||||
extern PFNCGGLGETMATRIXPARAMETERARRAYDCPROC cgGLGetMatrixParameterArraydc;
|
||||
extern PFNCGGLGETMATRIXPARAMETERARRAYDRPROC cgGLGetMatrixParameterArraydr;
|
||||
extern PFNCGGLSETTEXTUREPARAMETERPROC cgGLSetTextureParameter;
|
||||
extern PFNCGGLGETTEXTUREPARAMETERPROC cgGLGetTextureParameter;
|
||||
extern PFNCGGLENABLETEXTUREPARAMETERPROC cgGLEnableTextureParameter;
|
||||
extern PFNCGGLDISABLETEXTUREPARAMETERPROC cgGLDisableTextureParameter;
|
||||
extern PFNCGGLGETTEXTUREENUMPROC cgGLGetTextureEnum;
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool init_explicit_Cg();
|
||||
|
||||
#endif
|
||||
|
||||
529
neo/renderer/draw_arb.cpp
Normal file
529
neo/renderer/draw_arb.cpp
Normal file
@@ -0,0 +1,529 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
/*
|
||||
|
||||
with standard calls, we can't do bump mapping or vertex colors with
|
||||
shader colors
|
||||
|
||||
2 texture units:
|
||||
|
||||
falloff
|
||||
--
|
||||
light cube
|
||||
bump
|
||||
--
|
||||
light projection
|
||||
diffuse
|
||||
|
||||
|
||||
3 texture units:
|
||||
|
||||
light cube
|
||||
bump
|
||||
--
|
||||
falloff
|
||||
light projection
|
||||
diffuse
|
||||
|
||||
|
||||
5 texture units:
|
||||
|
||||
light cube
|
||||
bump
|
||||
falloff
|
||||
light projection
|
||||
diffuse
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_ARB_DrawInteraction
|
||||
|
||||
backEnd.vLight
|
||||
|
||||
backEnd.depthFunc must be equal for alpha tested surfaces to work right,
|
||||
it is set to lessThan for blended transparent surfaces
|
||||
|
||||
==================
|
||||
*/
|
||||
static void RB_ARB_DrawInteraction( const drawInteraction_t *din ) {
|
||||
const drawSurf_t *surf = din->surf;
|
||||
const srfTriangles_t *tri = din->surf->geo;
|
||||
|
||||
// set the vertex arrays, which may not all be enabled on a given pass
|
||||
idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache );
|
||||
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
|
||||
GL_SelectTexture( 0 );
|
||||
qglTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), (void *)&ac->st );
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// bump / falloff
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
// render light falloff * bumpmap lighting
|
||||
|
||||
//
|
||||
// draw light falloff to the alpha channel
|
||||
//
|
||||
GL_State( GLS_COLORMASK | GLS_DEPTHMASK | backEnd.depthFunc );
|
||||
|
||||
qglColor3f( 1, 1, 1 );
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglEnable( GL_TEXTURE_GEN_S );
|
||||
qglTexGenfv( GL_S, GL_OBJECT_PLANE, din->lightProjection[3].ToFloatPtr() );
|
||||
qglTexCoord2f( 0, 0.5 );
|
||||
|
||||
// ATI R100 can't do partial texgens
|
||||
#define NO_MIXED_TEXGEN
|
||||
|
||||
#ifdef NO_MIXED_TEXGEN
|
||||
idVec4 plane;
|
||||
plane[0] = 0;
|
||||
plane[1] = 0;
|
||||
plane[2] = 0;
|
||||
plane[3] = 0.5;
|
||||
qglEnable( GL_TEXTURE_GEN_T );
|
||||
qglTexGenfv( GL_T, GL_OBJECT_PLANE, plane.ToFloatPtr() );
|
||||
|
||||
plane[0] = 0;
|
||||
plane[1] = 0;
|
||||
plane[2] = 0;
|
||||
plane[3] = 1;
|
||||
qglEnable( GL_TEXTURE_GEN_Q );
|
||||
qglTexGenfv( GL_Q, GL_OBJECT_PLANE, plane.ToFloatPtr() );
|
||||
|
||||
#endif
|
||||
|
||||
din->lightFalloffImage->Bind();
|
||||
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
qglDisable( GL_TEXTURE_GEN_S );
|
||||
#ifdef NO_MIXED_TEXGEN
|
||||
qglDisable( GL_TEXTURE_GEN_T );
|
||||
qglDisable( GL_TEXTURE_GEN_Q );
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK
|
||||
| backEnd.depthFunc );
|
||||
// the texccords are the non-normalized vector towards the light origin
|
||||
GL_SelectTexture( 0 );
|
||||
globalImages->normalCubeMapImage->Bind();
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglTexCoordPointer( 3, GL_FLOAT, sizeof( lightingCache_t ), ((lightingCache_t *)vertexCache.Position(tri->lightingCache))->localLightVector.ToFloatPtr() );
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
return;
|
||||
#endif
|
||||
|
||||
// we can't do bump mapping with standard calls, so skip it
|
||||
if ( glConfig.envDot3Available && glConfig.cubeMapAvailable ) {
|
||||
//
|
||||
// draw the bump map result onto the alpha channel
|
||||
//
|
||||
GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ZERO | GLS_COLORMASK | GLS_DEPTHMASK
|
||||
| backEnd.depthFunc );
|
||||
|
||||
// texture 0 will be the per-surface bump map
|
||||
GL_SelectTexture( 0 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
// FIXME: matrix work! RB_BindStageTexture( surfaceRegs, &surfaceStage->texture, surf );
|
||||
din->bumpImage->Bind();
|
||||
|
||||
// texture 1 is the normalization cube map
|
||||
// the texccords are the non-normalized vector towards the light origin
|
||||
GL_SelectTexture( 1 );
|
||||
if ( din->ambientLight ) {
|
||||
globalImages->ambientNormalMap->Bind(); // fixed value
|
||||
} else {
|
||||
globalImages->normalCubeMapImage->Bind();
|
||||
}
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglTexCoordPointer( 3, GL_FLOAT, sizeof( lightingCache_t ), ((lightingCache_t *)vertexCache.Position(tri->lightingCache))->localLightVector.ToFloatPtr() );
|
||||
|
||||
// I just want alpha = Dot( texture0, texture1 )
|
||||
GL_TexEnv( GL_COMBINE_ARB );
|
||||
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGBA_ARB );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1 );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1 );
|
||||
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
GL_TexEnv( GL_MODULATE );
|
||||
|
||||
globalImages->BindNull();
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
GL_SelectTexture( 0 );
|
||||
// RB_FinishStageTexture( &surfaceStage->texture, surf );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// projected light / surface color for diffuse maps
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
// don't trash alpha
|
||||
GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_ALPHAMASK | GLS_DEPTHMASK
|
||||
| backEnd.depthFunc );
|
||||
|
||||
// texture 0 will get the surface color texture
|
||||
GL_SelectTexture( 0 );
|
||||
|
||||
// select the vertex color source
|
||||
if ( din->vertexColor == SVC_IGNORE ) {
|
||||
qglColor4fv( din->diffuseColor.ToFloatPtr() );
|
||||
} else {
|
||||
// FIXME: does this not get diffuseColor blended in?
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), (void *)&ac->color );
|
||||
qglEnableClientState( GL_COLOR_ARRAY );
|
||||
|
||||
if ( din->vertexColor == SVC_INVERSE_MODULATE ) {
|
||||
GL_TexEnv( GL_COMBINE_ARB );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_ONE_MINUS_SRC_COLOR );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
// FIXME: does this not get the texture matrix?
|
||||
// RB_BindStageTexture( surfaceRegs, &surfaceStage->texture, surf );
|
||||
din->diffuseImage->Bind();
|
||||
|
||||
// texture 1 will get the light projected texture
|
||||
GL_SelectTexture( 1 );
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglEnable( GL_TEXTURE_GEN_S );
|
||||
qglEnable( GL_TEXTURE_GEN_T );
|
||||
qglEnable( GL_TEXTURE_GEN_Q );
|
||||
qglTexGenfv( GL_S, GL_OBJECT_PLANE, din->lightProjection[0].ToFloatPtr() );
|
||||
qglTexGenfv( GL_T, GL_OBJECT_PLANE, din->lightProjection[1].ToFloatPtr() );
|
||||
qglTexGenfv( GL_Q, GL_OBJECT_PLANE, din->lightProjection[2].ToFloatPtr() );
|
||||
|
||||
din->lightImage->Bind();
|
||||
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
qglDisable( GL_TEXTURE_GEN_S );
|
||||
qglDisable( GL_TEXTURE_GEN_T );
|
||||
qglDisable( GL_TEXTURE_GEN_Q );
|
||||
|
||||
globalImages->BindNull();
|
||||
GL_SelectTexture( 0 );
|
||||
|
||||
if ( din->vertexColor != SVC_IGNORE ) {
|
||||
qglDisableClientState( GL_COLOR_ARRAY );
|
||||
GL_TexEnv( GL_MODULATE );
|
||||
}
|
||||
|
||||
// RB_FinishStageTexture( &surfaceStage->texture, surf );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_ARB_DrawThreeTextureInteraction
|
||||
|
||||
Used by radeon R100 and Intel graphics parts
|
||||
|
||||
backEnd.vLight
|
||||
|
||||
backEnd.depthFunc must be equal for alpha tested surfaces to work right,
|
||||
it is set to lessThan for blended transparent surfaces
|
||||
|
||||
==================
|
||||
*/
|
||||
static void RB_ARB_DrawThreeTextureInteraction( const drawInteraction_t *din ) {
|
||||
const drawSurf_t *surf = din->surf;
|
||||
const srfTriangles_t *tri = din->surf->geo;
|
||||
|
||||
// set the vertex arrays, which may not all be enabled on a given pass
|
||||
idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache );
|
||||
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
|
||||
GL_SelectTexture( 0 );
|
||||
qglTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), (void *)&ac->st );
|
||||
qglColor3f( 1, 1, 1 );
|
||||
|
||||
//
|
||||
// bump map dot cubeMap into the alpha channel
|
||||
//
|
||||
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_COLORMASK | GLS_DEPTHMASK
|
||||
| backEnd.depthFunc );
|
||||
|
||||
// texture 0 will be the per-surface bump map
|
||||
GL_SelectTexture( 0 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
// FIXME: matrix work! RB_BindStageTexture( surfaceRegs, &surfaceStage->texture, surf );
|
||||
din->bumpImage->Bind();
|
||||
|
||||
// texture 1 is the normalization cube map
|
||||
// the texccords are the non-normalized vector towards the light origin
|
||||
GL_SelectTexture( 1 );
|
||||
if ( din->ambientLight ) {
|
||||
globalImages->ambientNormalMap->Bind(); // fixed value
|
||||
} else {
|
||||
globalImages->normalCubeMapImage->Bind();
|
||||
}
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglTexCoordPointer( 3, GL_FLOAT, sizeof( lightingCache_t ), ((lightingCache_t *)vertexCache.Position(tri->lightingCache))->localLightVector.ToFloatPtr() );
|
||||
|
||||
// I just want alpha = Dot( texture0, texture1 )
|
||||
GL_TexEnv( GL_COMBINE_ARB );
|
||||
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGBA_ARB );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1 );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1 );
|
||||
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
GL_TexEnv( GL_MODULATE );
|
||||
|
||||
globalImages->BindNull();
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
GL_SelectTexture( 0 );
|
||||
// RB_FinishStageTexture( &surfaceStage->texture, surf );
|
||||
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// light falloff / projected light / surface color for diffuse maps
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
// multiply result by alpha, but don't trash alpha
|
||||
GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_ALPHAMASK | GLS_DEPTHMASK
|
||||
| backEnd.depthFunc );
|
||||
|
||||
// texture 0 will get the surface color texture
|
||||
GL_SelectTexture( 0 );
|
||||
|
||||
// select the vertex color source
|
||||
if ( din->vertexColor == SVC_IGNORE ) {
|
||||
qglColor4fv( din->diffuseColor.ToFloatPtr() );
|
||||
} else {
|
||||
// FIXME: does this not get diffuseColor blended in?
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), (void *)&ac->color );
|
||||
qglEnableClientState( GL_COLOR_ARRAY );
|
||||
|
||||
if ( din->vertexColor == SVC_INVERSE_MODULATE ) {
|
||||
GL_TexEnv( GL_COMBINE_ARB );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_ONE_MINUS_SRC_COLOR );
|
||||
qglTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
// FIXME: does this not get the texture matrix?
|
||||
// RB_BindStageTexture( surfaceRegs, &surfaceStage->texture, surf );
|
||||
din->diffuseImage->Bind();
|
||||
|
||||
// texture 1 will get the light projected texture
|
||||
GL_SelectTexture( 1 );
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglEnable( GL_TEXTURE_GEN_S );
|
||||
qglEnable( GL_TEXTURE_GEN_T );
|
||||
qglEnable( GL_TEXTURE_GEN_Q );
|
||||
qglTexGenfv( GL_S, GL_OBJECT_PLANE, din->lightProjection[0].ToFloatPtr() );
|
||||
qglTexGenfv( GL_T, GL_OBJECT_PLANE, din->lightProjection[1].ToFloatPtr() );
|
||||
qglTexGenfv( GL_Q, GL_OBJECT_PLANE, din->lightProjection[2].ToFloatPtr() );
|
||||
din->lightImage->Bind();
|
||||
|
||||
// texture 2 will get the light falloff texture
|
||||
GL_SelectTexture( 2 );
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglEnable( GL_TEXTURE_GEN_S );
|
||||
qglEnable( GL_TEXTURE_GEN_T );
|
||||
qglEnable( GL_TEXTURE_GEN_Q );
|
||||
|
||||
qglTexGenfv( GL_S, GL_OBJECT_PLANE, din->lightProjection[3].ToFloatPtr() );
|
||||
|
||||
idVec4 plane;
|
||||
plane[0] = 0;
|
||||
plane[1] = 0;
|
||||
plane[2] = 0;
|
||||
plane[3] = 0.5;
|
||||
qglTexGenfv( GL_T, GL_OBJECT_PLANE, plane.ToFloatPtr() );
|
||||
|
||||
plane[0] = 0;
|
||||
plane[1] = 0;
|
||||
plane[2] = 0;
|
||||
plane[3] = 1;
|
||||
qglTexGenfv( GL_Q, GL_OBJECT_PLANE, plane.ToFloatPtr() );
|
||||
|
||||
din->lightFalloffImage->Bind();
|
||||
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
qglDisable( GL_TEXTURE_GEN_S );
|
||||
qglDisable( GL_TEXTURE_GEN_T );
|
||||
qglDisable( GL_TEXTURE_GEN_Q );
|
||||
globalImages->BindNull();
|
||||
|
||||
GL_SelectTexture( 1 );
|
||||
qglDisable( GL_TEXTURE_GEN_S );
|
||||
qglDisable( GL_TEXTURE_GEN_T );
|
||||
qglDisable( GL_TEXTURE_GEN_Q );
|
||||
globalImages->BindNull();
|
||||
|
||||
GL_SelectTexture( 0 );
|
||||
|
||||
if ( din->vertexColor != SVC_IGNORE ) {
|
||||
qglDisableClientState( GL_COLOR_ARRAY );
|
||||
GL_TexEnv( GL_MODULATE );
|
||||
}
|
||||
|
||||
// RB_FinishStageTexture( &surfaceStage->texture, surf );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_CreateDrawInteractions
|
||||
==================
|
||||
*/
|
||||
static void RB_CreateDrawInteractions( const drawSurf_t *surf ) {
|
||||
if ( !surf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// force a space calculation
|
||||
backEnd.currentSpace = NULL;
|
||||
|
||||
if ( r_useTripleTextureARB.GetBool() && glConfig.maxTextureUnits >= 3 ) {
|
||||
for ( ; surf ; surf = surf->nextOnLight ) {
|
||||
// break it up into multiple primitive draw interactions if necessary
|
||||
RB_CreateSingleDrawInteractions( surf, RB_ARB_DrawThreeTextureInteraction );
|
||||
}
|
||||
} else {
|
||||
for ( ; surf ; surf = surf->nextOnLight ) {
|
||||
// break it up into multiple primitive draw interactions if necessary
|
||||
RB_CreateSingleDrawInteractions( surf, RB_ARB_DrawInteraction );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_RenderViewLight
|
||||
|
||||
==================
|
||||
*/
|
||||
static void RB_RenderViewLight( viewLight_t *vLight ) {
|
||||
backEnd.vLight = vLight;
|
||||
|
||||
// do fogging later
|
||||
if ( vLight->lightShader->IsFogLight() ) {
|
||||
return;
|
||||
}
|
||||
if ( vLight->lightShader->IsBlendLight() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
RB_LogComment( "---------- RB_RenderViewLight 0x%p ----------\n", vLight );
|
||||
|
||||
// clear the stencil buffer if needed
|
||||
if ( vLight->globalShadows || vLight->localShadows ) {
|
||||
backEnd.currentScissor = vLight->scissorRect;
|
||||
if ( r_useScissor.GetBool() ) {
|
||||
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
|
||||
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
|
||||
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
|
||||
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
|
||||
}
|
||||
qglClear( GL_STENCIL_BUFFER_BIT );
|
||||
} else {
|
||||
// no shadows, so no need to read or write the stencil buffer
|
||||
// we might in theory want to use GL_ALWAYS instead of disabling
|
||||
// completely, to satisfy the invarience rules
|
||||
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
||||
}
|
||||
|
||||
backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
|
||||
RB_StencilShadowPass( vLight->globalShadows );
|
||||
RB_CreateDrawInteractions( vLight->localInteractions );
|
||||
RB_StencilShadowPass( vLight->localShadows );
|
||||
RB_CreateDrawInteractions( vLight->globalInteractions );
|
||||
|
||||
if ( r_skipTranslucent.GetBool() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// disable stencil testing for translucent interactions, because
|
||||
// the shadow isn't calculated at their point, and the shadow
|
||||
// behind them may be depth fighting with a back side, so there
|
||||
// isn't any reasonable thing to do
|
||||
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
||||
backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
|
||||
RB_CreateDrawInteractions( vLight->translucentInteractions );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_ARB_DrawInteractions
|
||||
==================
|
||||
*/
|
||||
void RB_ARB_DrawInteractions( void ) {
|
||||
qglEnable( GL_STENCIL_TEST );
|
||||
|
||||
for ( viewLight_t *vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
|
||||
RB_RenderViewLight( vLight );
|
||||
}
|
||||
}
|
||||
|
||||
537
neo/renderer/draw_arb2.cpp
Normal file
537
neo/renderer/draw_arb2.cpp
Normal file
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
#include "cg_explicit.h"
|
||||
|
||||
CGcontext cg_context;
|
||||
|
||||
static void cg_error_callback( void ) {
|
||||
CGerror i = cgGetError();
|
||||
common->Printf( "Cg error (%d): %s\n", i, cgGetErrorString(i) );
|
||||
}
|
||||
|
||||
/*
|
||||
=========================================================================================
|
||||
|
||||
GENERAL INTERACTION RENDERING
|
||||
|
||||
=========================================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
====================
|
||||
GL_SelectTextureNoClient
|
||||
====================
|
||||
*/
|
||||
static void GL_SelectTextureNoClient( int unit ) {
|
||||
backEnd.glState.currenttmu = unit;
|
||||
qglActiveTextureARB( GL_TEXTURE0_ARB + unit );
|
||||
RB_LogComment( "glActiveTextureARB( %i )\n", unit );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_ARB2_DrawInteraction
|
||||
==================
|
||||
*/
|
||||
void RB_ARB2_DrawInteraction( const drawInteraction_t *din ) {
|
||||
// load all the vertex program parameters
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, din->localLightOrigin.ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_VIEW_ORIGIN, din->localViewOrigin.ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_S, din->lightProjection[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_T, din->lightProjection[1].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_Q, din->lightProjection[2].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_FALLOFF_S, din->lightProjection[3].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_S, din->bumpMatrix[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_T, din->bumpMatrix[1].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_S, din->diffuseMatrix[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_T, din->diffuseMatrix[1].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_S, din->specularMatrix[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_T, din->specularMatrix[1].ToFloatPtr() );
|
||||
|
||||
// testing fragment based normal mapping
|
||||
if ( r_testARBProgram.GetBool() ) {
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, din->localLightOrigin.ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, din->localViewOrigin.ToFloatPtr() );
|
||||
}
|
||||
|
||||
static const float zero[4] = { 0, 0, 0, 0 };
|
||||
static const float one[4] = { 1, 1, 1, 1 };
|
||||
static const float negOne[4] = { -1, -1, -1, -1 };
|
||||
|
||||
switch ( din->vertexColor ) {
|
||||
case SVC_IGNORE:
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, zero );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
|
||||
break;
|
||||
case SVC_MODULATE:
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, one );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, zero );
|
||||
break;
|
||||
case SVC_INVERSE_MODULATE:
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, negOne );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
|
||||
break;
|
||||
}
|
||||
|
||||
// set the constant colors
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, din->diffuseColor.ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, din->specularColor.ToFloatPtr() );
|
||||
|
||||
// set the textures
|
||||
|
||||
// texture 1 will be the per-surface bump map
|
||||
GL_SelectTextureNoClient( 1 );
|
||||
din->bumpImage->Bind();
|
||||
|
||||
// texture 2 will be the light falloff texture
|
||||
GL_SelectTextureNoClient( 2 );
|
||||
din->lightFalloffImage->Bind();
|
||||
|
||||
// texture 3 will be the light projection texture
|
||||
GL_SelectTextureNoClient( 3 );
|
||||
din->lightImage->Bind();
|
||||
|
||||
// texture 4 is the per-surface diffuse map
|
||||
GL_SelectTextureNoClient( 4 );
|
||||
din->diffuseImage->Bind();
|
||||
|
||||
// texture 5 is the per-surface specular map
|
||||
GL_SelectTextureNoClient( 5 );
|
||||
din->specularImage->Bind();
|
||||
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( din->surf->geo );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
RB_ARB2_CreateDrawInteractions
|
||||
|
||||
=============
|
||||
*/
|
||||
void RB_ARB2_CreateDrawInteractions( const drawSurf_t *surf ) {
|
||||
if ( !surf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// perform setup here that will be constant for all interactions
|
||||
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc );
|
||||
|
||||
// bind the vertex program
|
||||
if ( r_testARBProgram.GetBool() ) {
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_TEST );
|
||||
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST );
|
||||
} else {
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION );
|
||||
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION );
|
||||
}
|
||||
|
||||
qglEnable(GL_VERTEX_PROGRAM_ARB);
|
||||
qglEnable(GL_FRAGMENT_PROGRAM_ARB);
|
||||
|
||||
// enable the vertex arrays
|
||||
qglEnableVertexAttribArrayARB( 8 );
|
||||
qglEnableVertexAttribArrayARB( 9 );
|
||||
qglEnableVertexAttribArrayARB( 10 );
|
||||
qglEnableVertexAttribArrayARB( 11 );
|
||||
qglEnableClientState( GL_COLOR_ARRAY );
|
||||
|
||||
// texture 0 is the normalization cube map for the vector towards the light
|
||||
GL_SelectTextureNoClient( 0 );
|
||||
if ( backEnd.vLight->lightShader->IsAmbientLight() ) {
|
||||
globalImages->ambientNormalMap->Bind();
|
||||
} else {
|
||||
globalImages->normalCubeMapImage->Bind();
|
||||
}
|
||||
|
||||
// texture 6 is the specular lookup table
|
||||
GL_SelectTextureNoClient( 6 );
|
||||
if ( r_testARBProgram.GetBool() ) {
|
||||
globalImages->specular2DTableImage->Bind(); // variable specularity in alpha channel
|
||||
} else {
|
||||
globalImages->specularTableImage->Bind();
|
||||
}
|
||||
|
||||
|
||||
for ( ; surf ; surf=surf->nextOnLight ) {
|
||||
// perform setup here that will not change over multiple interaction passes
|
||||
|
||||
// set the vertex pointers
|
||||
idDrawVert *ac = (idDrawVert *)vertexCache.Position( surf->geo->ambientCache );
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), ac->color );
|
||||
qglVertexAttribPointerARB( 11, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->normal.ToFloatPtr() );
|
||||
qglVertexAttribPointerARB( 10, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() );
|
||||
qglVertexAttribPointerARB( 9, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() );
|
||||
qglVertexAttribPointerARB( 8, 2, GL_FLOAT, false, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
|
||||
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
|
||||
|
||||
// this may cause RB_ARB2_DrawInteraction to be exacuted multiple
|
||||
// times with different colors and images if the surface or light have multiple layers
|
||||
RB_CreateSingleDrawInteractions( surf, RB_ARB2_DrawInteraction );
|
||||
}
|
||||
|
||||
qglDisableVertexAttribArrayARB( 8 );
|
||||
qglDisableVertexAttribArrayARB( 9 );
|
||||
qglDisableVertexAttribArrayARB( 10 );
|
||||
qglDisableVertexAttribArrayARB( 11 );
|
||||
qglDisableClientState( GL_COLOR_ARRAY );
|
||||
|
||||
// disable features
|
||||
GL_SelectTextureNoClient( 6 );
|
||||
globalImages->BindNull();
|
||||
|
||||
GL_SelectTextureNoClient( 5 );
|
||||
globalImages->BindNull();
|
||||
|
||||
GL_SelectTextureNoClient( 4 );
|
||||
globalImages->BindNull();
|
||||
|
||||
GL_SelectTextureNoClient( 3 );
|
||||
globalImages->BindNull();
|
||||
|
||||
GL_SelectTextureNoClient( 2 );
|
||||
globalImages->BindNull();
|
||||
|
||||
GL_SelectTextureNoClient( 1 );
|
||||
globalImages->BindNull();
|
||||
|
||||
backEnd.glState.currenttmu = -1;
|
||||
GL_SelectTexture( 0 );
|
||||
|
||||
qglDisable(GL_VERTEX_PROGRAM_ARB);
|
||||
qglDisable(GL_FRAGMENT_PROGRAM_ARB);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_ARB2_DrawInteractions
|
||||
==================
|
||||
*/
|
||||
void RB_ARB2_DrawInteractions( void ) {
|
||||
viewLight_t *vLight;
|
||||
const idMaterial *lightShader;
|
||||
|
||||
GL_SelectTexture( 0 );
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
//
|
||||
// for each light, perform adding and shadowing
|
||||
//
|
||||
for ( vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
|
||||
backEnd.vLight = vLight;
|
||||
|
||||
// do fogging later
|
||||
if ( vLight->lightShader->IsFogLight() ) {
|
||||
continue;
|
||||
}
|
||||
if ( vLight->lightShader->IsBlendLight() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !vLight->localInteractions && !vLight->globalInteractions
|
||||
&& !vLight->translucentInteractions ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lightShader = vLight->lightShader;
|
||||
|
||||
// clear the stencil buffer if needed
|
||||
if ( vLight->globalShadows || vLight->localShadows ) {
|
||||
backEnd.currentScissor = vLight->scissorRect;
|
||||
if ( r_useScissor.GetBool() ) {
|
||||
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
|
||||
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
|
||||
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
|
||||
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
|
||||
}
|
||||
qglClear( GL_STENCIL_BUFFER_BIT );
|
||||
} else {
|
||||
// no shadows, so no need to read or write the stencil buffer
|
||||
// we might in theory want to use GL_ALWAYS instead of disabling
|
||||
// completely, to satisfy the invarience rules
|
||||
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
||||
}
|
||||
|
||||
if ( r_useShadowVertexProgram.GetBool() ) {
|
||||
qglEnable( GL_VERTEX_PROGRAM_ARB );
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
|
||||
RB_StencilShadowPass( vLight->globalShadows );
|
||||
RB_ARB2_CreateDrawInteractions( vLight->localInteractions );
|
||||
qglEnable( GL_VERTEX_PROGRAM_ARB );
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
|
||||
RB_StencilShadowPass( vLight->localShadows );
|
||||
RB_ARB2_CreateDrawInteractions( vLight->globalInteractions );
|
||||
qglDisable( GL_VERTEX_PROGRAM_ARB ); // if there weren't any globalInteractions, it would have stayed on
|
||||
} else {
|
||||
RB_StencilShadowPass( vLight->globalShadows );
|
||||
RB_ARB2_CreateDrawInteractions( vLight->localInteractions );
|
||||
RB_StencilShadowPass( vLight->localShadows );
|
||||
RB_ARB2_CreateDrawInteractions( vLight->globalInteractions );
|
||||
}
|
||||
|
||||
// translucent surfaces never get stencil shadowed
|
||||
if ( r_skipTranslucent.GetBool() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
||||
|
||||
backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
|
||||
RB_ARB2_CreateDrawInteractions( vLight->translucentInteractions );
|
||||
|
||||
backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
|
||||
}
|
||||
|
||||
// disable stencil shadow test
|
||||
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
||||
|
||||
GL_SelectTexture( 0 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
}
|
||||
|
||||
//===================================================================================
|
||||
|
||||
|
||||
typedef struct {
|
||||
GLenum target;
|
||||
GLuint ident;
|
||||
char name[64];
|
||||
} progDef_t;
|
||||
|
||||
static const int MAX_GLPROGS = 200;
|
||||
|
||||
// a single file can have both a vertex program and a fragment program
|
||||
static progDef_t progs[MAX_GLPROGS] = {
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_TEST, "test.vfp" },
|
||||
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST, "test.vfp" },
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION, "interaction.vfp" },
|
||||
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION, "interaction.vfp" },
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_BUMPY_ENVIRONMENT, "bumpyEnvironment.vfp" },
|
||||
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_BUMPY_ENVIRONMENT, "bumpyEnvironment.vfp" },
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_AMBIENT, "ambientLight.vfp" },
|
||||
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_AMBIENT, "ambientLight.vfp" },
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW, "shadow.vp" },
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_R200_INTERACTION, "R200_interaction.vp" },
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_NV20_BUMP_AND_LIGHT, "nv20_bumpAndLight.vp" },
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_NV20_DIFFUSE_COLOR, "nv20_diffuseColor.vp" },
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_NV20_SPECULAR_COLOR, "nv20_specularColor.vp" },
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_NV20_DIFFUSE_AND_SPECULAR_COLOR, "nv20_diffuseAndSpecularColor.vp" },
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_ENVIRONMENT, "environment.vfp" },
|
||||
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_ENVIRONMENT, "environment.vfp" },
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_GLASSWARP, "arbVP_glasswarp.txt" },
|
||||
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_GLASSWARP, "arbFP_glasswarp.txt" },
|
||||
|
||||
// additional programs can be dynamically specified in materials
|
||||
};
|
||||
|
||||
/*
|
||||
=================
|
||||
R_LoadARBProgram
|
||||
=================
|
||||
*/
|
||||
void R_LoadARBProgram( int progIndex ) {
|
||||
int ofs;
|
||||
int err;
|
||||
idStr fullPath = "glprogs/";
|
||||
fullPath += progs[progIndex].name;
|
||||
char *fileBuffer;
|
||||
char *buffer;
|
||||
char *start, *end;
|
||||
|
||||
common->Printf( "%s", fullPath.c_str() );
|
||||
|
||||
// load the program even if we don't support it, so
|
||||
// fs_copyfiles can generate cross-platform data dumps
|
||||
fileSystem->ReadFile( fullPath.c_str(), (void **)&fileBuffer, NULL );
|
||||
if ( !fileBuffer ) {
|
||||
common->Printf( ": File not found\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// copy to stack memory and free
|
||||
buffer = (char *)_alloca( strlen( fileBuffer ) + 1 );
|
||||
strcpy( buffer, fileBuffer );
|
||||
fileSystem->FreeFile( fileBuffer );
|
||||
|
||||
if ( !glConfig.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// submit the program string at start to GL
|
||||
//
|
||||
if ( progs[progIndex].ident == 0 ) {
|
||||
// allocate a new identifier for this program
|
||||
progs[progIndex].ident = PROG_USER + progIndex;
|
||||
}
|
||||
|
||||
// vertex and fragment programs can both be present in a single file, so
|
||||
// scan for the proper header to be the start point, and stamp a 0 in after the end
|
||||
|
||||
if ( progs[progIndex].target == GL_VERTEX_PROGRAM_ARB ) {
|
||||
if ( !glConfig.ARBVertexProgramAvailable ) {
|
||||
common->Printf( ": GL_VERTEX_PROGRAM_ARB not available\n" );
|
||||
return;
|
||||
}
|
||||
start = strstr( (char *)buffer, "!!ARBvp" );
|
||||
}
|
||||
if ( progs[progIndex].target == GL_FRAGMENT_PROGRAM_ARB ) {
|
||||
if ( !glConfig.ARBFragmentProgramAvailable ) {
|
||||
common->Printf( ": GL_FRAGMENT_PROGRAM_ARB not available\n" );
|
||||
return;
|
||||
}
|
||||
start = strstr( (char *)buffer, "!!ARBfp" );
|
||||
}
|
||||
if ( !start ) {
|
||||
common->Printf( ": !!ARB not found\n" );
|
||||
return;
|
||||
}
|
||||
end = strstr( start, "END" );
|
||||
|
||||
if ( !end ) {
|
||||
common->Printf( ": END not found\n" );
|
||||
return;
|
||||
}
|
||||
end[3] = 0;
|
||||
|
||||
qglBindProgramARB( progs[progIndex].target, progs[progIndex].ident );
|
||||
qglGetError();
|
||||
|
||||
qglProgramStringARB( progs[progIndex].target, GL_PROGRAM_FORMAT_ASCII_ARB,
|
||||
strlen( start ), (unsigned char *)start );
|
||||
|
||||
err = qglGetError();
|
||||
qglGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, (GLint *)&ofs );
|
||||
if ( err == GL_INVALID_OPERATION ) {
|
||||
const GLubyte *str = qglGetString( GL_PROGRAM_ERROR_STRING_ARB );
|
||||
common->Printf( "\nGL_PROGRAM_ERROR_STRING_ARB: %s\n", str );
|
||||
if ( ofs < 0 ) {
|
||||
common->Printf( "GL_PROGRAM_ERROR_POSITION_ARB < 0 with error\n" );
|
||||
} else if ( ofs >= (int)strlen( (char *)start ) ) {
|
||||
common->Printf( "error at end of program\n" );
|
||||
} else {
|
||||
common->Printf( "error at %i:\n%s", ofs, start + ofs );
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ( ofs != -1 ) {
|
||||
common->Printf( "\nGL_PROGRAM_ERROR_POSITION_ARB != -1 without error\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
common->Printf( "\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
R_FindARBProgram
|
||||
|
||||
Returns a GL identifier that can be bound to the given target, parsing
|
||||
a text file if it hasn't already been loaded.
|
||||
==================
|
||||
*/
|
||||
int R_FindARBProgram( GLenum target, const char *program ) {
|
||||
int i;
|
||||
idStr stripped = program;
|
||||
|
||||
stripped.StripFileExtension();
|
||||
|
||||
// see if it is already loaded
|
||||
for ( i = 0 ; progs[i].name[0] ; i++ ) {
|
||||
if ( progs[i].target != target ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
idStr compare = progs[i].name;
|
||||
compare.StripFileExtension();
|
||||
|
||||
if ( !idStr::Icmp( stripped.c_str(), compare.c_str() ) ) {
|
||||
return progs[i].ident;
|
||||
}
|
||||
}
|
||||
|
||||
if ( i == MAX_GLPROGS ) {
|
||||
common->Error( "R_FindARBProgram: MAX_GLPROGS" );
|
||||
}
|
||||
|
||||
// add it to the list and load it
|
||||
progs[i].ident = (program_t)0; // will be gen'd by R_LoadARBProgram
|
||||
progs[i].target = target;
|
||||
strncpy( progs[i].name, program, sizeof( progs[i].name ) - 1 );
|
||||
|
||||
R_LoadARBProgram( i );
|
||||
|
||||
return progs[i].ident;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
R_ReloadARBPrograms_f
|
||||
==================
|
||||
*/
|
||||
void R_ReloadARBPrograms_f( const idCmdArgs &args ) {
|
||||
int i;
|
||||
|
||||
common->Printf( "----- R_ReloadARBPrograms -----\n" );
|
||||
for ( i = 0 ; progs[i].name[0] ; i++ ) {
|
||||
R_LoadARBProgram( i );
|
||||
}
|
||||
common->Printf( "-------------------------------\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
R_ARB2_Init
|
||||
|
||||
==================
|
||||
*/
|
||||
void R_ARB2_Init( void ) {
|
||||
glConfig.allowARB2Path = false;
|
||||
|
||||
common->Printf( "---------- R_ARB2_Init ----------\n" );
|
||||
|
||||
if ( !glConfig.ARBVertexProgramAvailable || !glConfig.ARBFragmentProgramAvailable ) {
|
||||
common->Printf( "Not available.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
common->Printf( "Available.\n" );
|
||||
|
||||
common->Printf( "---------------------------------\n" );
|
||||
|
||||
glConfig.allowARB2Path = true;
|
||||
}
|
||||
|
||||
1725
neo/renderer/draw_common.cpp
Normal file
1725
neo/renderer/draw_common.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2618
neo/renderer/draw_exp.cpp
Normal file
2618
neo/renderer/draw_exp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
38
neo/renderer/draw_exp_stub.cpp
Normal file
38
neo/renderer/draw_exp_stub.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
void R_Exp_Init( void ) {
|
||||
common->Printf( "---------- R_Exp_Init -----------\n" );
|
||||
common->Printf( "Disabled at compile time.\n" );
|
||||
common->Printf( "---------------------------------\n" );
|
||||
}
|
||||
|
||||
void RB_Exp_DrawInteractions( void ) { }
|
||||
647
neo/renderer/draw_nv10.cpp
Normal file
647
neo/renderer/draw_nv10.cpp
Normal file
@@ -0,0 +1,647 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_RenderInteraction
|
||||
|
||||
backEnd.vLight
|
||||
backEnd.lightScale
|
||||
|
||||
|
||||
backEnd.depthFunc must be equal for alpha tested surfaces to work right,
|
||||
it is set to lessThan for blended transparent surfaces
|
||||
|
||||
This expects a bumpmap stage before a diffuse stage before a specular stage
|
||||
The material code is responsible for guaranteeing that, but conditional stages
|
||||
can still make it invalid.
|
||||
|
||||
you can't blend two bumpmaps, but you can change bump maps between
|
||||
blended diffuse / specular maps to get the same effect
|
||||
|
||||
|
||||
==================
|
||||
*/
|
||||
static void RB_RenderInteraction( const drawSurf_t *surf ) {
|
||||
const idMaterial *surfaceShader = surf->material;
|
||||
const float *surfaceRegs = surf->shaderRegisters;
|
||||
const viewLight_t *vLight = backEnd.vLight;
|
||||
const idMaterial *lightShader = vLight->lightShader;
|
||||
const float *lightRegs = vLight->shaderRegisters;
|
||||
static idPlane lightProject[4]; // reused across function calls
|
||||
const srfTriangles_t *tri = surf->geo;
|
||||
const shaderStage_t *lastBumpStage = NULL;
|
||||
|
||||
RB_LogComment( "---------- RB_RenderInteraction %s on %s ----------\n",
|
||||
lightShader->GetName(), surfaceShader->GetName() );
|
||||
|
||||
// change the matrix and light projection vectors if needed
|
||||
if ( surf->space != backEnd.currentSpace ) {
|
||||
backEnd.currentSpace = surf->space;
|
||||
qglLoadMatrixf( surf->space->modelViewMatrix );
|
||||
|
||||
for ( int i = 0 ; i < 4 ; i++ ) {
|
||||
R_GlobalPlaneToLocal( surf->space->modelMatrix, backEnd.vLight->lightProject[i], lightProject[i] );
|
||||
}
|
||||
}
|
||||
|
||||
// change the scissor if needed
|
||||
if ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals( surf->scissorRect ) ) {
|
||||
backEnd.currentScissor = surf->scissorRect;
|
||||
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
|
||||
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
|
||||
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
|
||||
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
|
||||
}
|
||||
|
||||
// hack depth range if needed
|
||||
if ( surf->space->weaponDepthHack ) {
|
||||
RB_EnterWeaponDepthHack();
|
||||
}
|
||||
|
||||
if ( surf->space->modelDepthHack != 0.0f ) {
|
||||
RB_EnterModelDepthHack( surf->space->modelDepthHack );
|
||||
}
|
||||
|
||||
|
||||
// set the vertex arrays, which may not all be enabled on a given pass
|
||||
idDrawVert *ac = (idDrawVert *)vertexCache.Position(tri->ambientCache);
|
||||
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
|
||||
GL_SelectTexture( 0 );
|
||||
qglTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), ac->color );
|
||||
|
||||
|
||||
// go through the individual stages
|
||||
for ( int i = 0 ; i < surfaceShader->GetNumStages() ; i++ ) {
|
||||
const shaderStage_t *surfaceStage = surfaceShader->GetStage( i );
|
||||
|
||||
// ignore ambient stages while drawing interactions
|
||||
if ( surfaceStage->lighting == SL_AMBIENT ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore stages that fail the condition
|
||||
if ( !surfaceRegs[ surfaceStage->conditionRegister ] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// bump / falloff
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
if ( surfaceStage->lighting == SL_BUMP ) {
|
||||
// render light falloff * bumpmap lighting
|
||||
|
||||
if ( surfaceStage->vertexColor != SVC_IGNORE ) {
|
||||
common->Printf( "shader %s: vertexColor on a bump stage\n",
|
||||
surfaceShader->GetName() );
|
||||
}
|
||||
|
||||
// check for RGBA modulations in the stage, which are also illegal?
|
||||
|
||||
// save the bump map stage for the specular calculation and diffuse
|
||||
// error checking
|
||||
lastBumpStage = surfaceStage;
|
||||
|
||||
//
|
||||
// ambient lights combine non-directional bump and falloff
|
||||
// and write to the alpha channel
|
||||
//
|
||||
if ( lightShader->IsAmbientLight() ) {
|
||||
GL_State( GLS_COLORMASK | GLS_DEPTHMASK | backEnd.depthFunc );
|
||||
|
||||
// texture 0 will be the per-surface bump map
|
||||
GL_SelectTexture( 0 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
RB_BindStageTexture( surfaceRegs, &surfaceStage->texture, surf );
|
||||
// development aid
|
||||
if ( r_skipBump.GetBool() ) {
|
||||
globalImages->flatNormalMap->Bind();
|
||||
}
|
||||
|
||||
// texture 1 will be the light falloff
|
||||
GL_SelectTexture( 1 );
|
||||
|
||||
qglEnable( GL_TEXTURE_GEN_S );
|
||||
qglTexGenfv( GL_S, GL_OBJECT_PLANE, lightProject[3].ToFloatPtr() );
|
||||
qglTexCoord2f( 0, 0.5 );
|
||||
vLight->falloffImage->Bind();
|
||||
|
||||
qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 2 );
|
||||
|
||||
// set the constant color to a bit of an angle
|
||||
qglCombinerParameterfvNV( GL_CONSTANT_COLOR0_NV, tr.ambientLightVector.ToFloatPtr() );
|
||||
|
||||
// stage 0 sets primary_color = bump dot constant color
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_CONSTANT_COLOR0_NV, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
|
||||
GL_PRIMARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 1 alpha sets primary_color = primary_color * falloff
|
||||
qglCombinerInputNV( GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_A_NV,
|
||||
GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE );
|
||||
qglCombinerInputNV( GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_B_NV,
|
||||
GL_TEXTURE1_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
|
||||
qglCombinerOutputNV( GL_COMBINER1_NV, GL_ALPHA,
|
||||
GL_PRIMARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// final combiner takes the result for the alpha channel
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_PRIMARY_COLOR_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
|
||||
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
globalImages->BindNull();
|
||||
qglDisable( GL_TEXTURE_GEN_S );
|
||||
|
||||
GL_SelectTexture( 0 );
|
||||
RB_FinishStageTexture( &surfaceStage->texture, surf );
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// draw light falloff to the alpha channel
|
||||
//
|
||||
GL_State( GLS_COLORMASK | GLS_DEPTHMASK | backEnd.depthFunc );
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglDisableClientState( GL_COLOR_ARRAY );
|
||||
qglEnable( GL_TEXTURE_GEN_S );
|
||||
qglTexGenfv( GL_S, GL_OBJECT_PLANE, lightProject[3].ToFloatPtr() );
|
||||
qglTexCoord2f( 0, 0.5 );
|
||||
vLight->falloffImage->Bind();
|
||||
|
||||
// make sure a combiner output doesn't step on the texture
|
||||
qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 1 );
|
||||
qglCombinerOutputNV( GL_COMBINER0_NV, GL_ALPHA,
|
||||
GL_DISCARD_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// final combiner
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_TEXTURE0_ARB,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
|
||||
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
qglDisable( GL_TEXTURE_GEN_S );
|
||||
|
||||
//
|
||||
// draw the bump map result onto the alpha channel
|
||||
//
|
||||
GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ZERO | GLS_COLORMASK | GLS_DEPTHMASK
|
||||
| backEnd.depthFunc );
|
||||
|
||||
// texture 0 will be the per-surface bump map
|
||||
GL_SelectTexture( 0 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
RB_BindStageTexture( surfaceRegs, &surfaceStage->texture, surf );
|
||||
|
||||
// texture 1 is the normalization cube map
|
||||
// the texccords are the non-normalized vector towards the light origin
|
||||
GL_SelectTexture( 1 );
|
||||
globalImages->normalCubeMapImage->Bind();
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglTexCoordPointer( 3, GL_FLOAT, sizeof( lightingCache_t ), ((lightingCache_t *)vertexCache.Position(tri->lightingCache))->localLightVector.ToFloatPtr() );
|
||||
|
||||
qglDisableClientState( GL_COLOR_ARRAY );
|
||||
|
||||
// program the nvidia register combiners
|
||||
// I just want alpha = Dot( texture0, texture1 )
|
||||
qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 1 );
|
||||
|
||||
// stage 0 rgb performs the dot product
|
||||
// SPARE0 = TEXTURE0 dot TEXTURE1
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
|
||||
GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// final combiner just takes the dot result and puts it in alpha
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_SPARE0_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_BLUE );
|
||||
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
globalImages->BindNull();
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
GL_SelectTexture( 0 );
|
||||
RB_FinishStageTexture( &surfaceStage->texture, surf );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( surfaceStage->lighting == SL_DIFFUSE ) {
|
||||
if ( !lastBumpStage ) {
|
||||
common->Printf( "shader %s: diffuse stage without a preceeding bumpmap stage\n",
|
||||
surfaceShader->GetName() );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// specular exponent modification of the bump / falloff
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
if ( surfaceStage->lighting == SL_SPECULAR ) {
|
||||
// put specular bump map into alpha channel, then treat as a diffuse
|
||||
|
||||
// allow the specular to be skipped as a user speed optimization
|
||||
if ( r_skipSpecular.GetBool() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ambient lights don't have specular
|
||||
if ( lightShader->IsAmbientLight() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !lastBumpStage ) {
|
||||
common->Printf( "shader %s: specular stage without a preceeding bumpmap stage\n",
|
||||
surfaceShader->GetName() );
|
||||
continue;
|
||||
}
|
||||
|
||||
GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_SRC_ALPHA | GLS_COLORMASK | GLS_DEPTHMASK
|
||||
| backEnd.depthFunc );
|
||||
|
||||
// texture 0 will be the per-surface bump map
|
||||
GL_SelectTexture( 0 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
RB_BindStageTexture( surfaceRegs, &lastBumpStage->texture, surf );
|
||||
|
||||
// development aid
|
||||
if ( r_skipBump.GetBool() ) {
|
||||
globalImages->flatNormalMap->Bind();
|
||||
}
|
||||
|
||||
// texture 1 is the normalization cube map
|
||||
// indexed by the dynamic halfangle texcoords
|
||||
GL_SelectTexture( 1 );
|
||||
globalImages->normalCubeMapImage->Bind();
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglTexCoordPointer( 4, GL_FLOAT, 0, vertexCache.Position( surf->dynamicTexCoords ) );
|
||||
|
||||
// program the nvidia register combiners
|
||||
qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 2 );
|
||||
|
||||
// stage 0 rgb performs the dot product
|
||||
// GL_PRIMARY_COLOR_NV = ( TEXTURE0 dot TEXTURE1 - 0.5 ) * 2
|
||||
// the scale and bias steepen the specular curve
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
|
||||
GL_PRIMARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_TRUE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 0 alpha does nothing
|
||||
qglCombinerOutputNV( GL_COMBINER0_NV, GL_ALPHA,
|
||||
GL_DISCARD_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 1 rgb does nothing
|
||||
qglCombinerOutputNV( GL_COMBINER1_NV, GL_RGB,
|
||||
GL_PRIMARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 1 alpha takes bump * bump
|
||||
// PRIMARY_COLOR = ( GL_PRIMARY_COLOR_NV * GL_PRIMARY_COLOR_NV - 0.5 ) * 2
|
||||
// the scale and bias steepen the specular curve
|
||||
qglCombinerInputNV( GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_A_NV,
|
||||
GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE );
|
||||
qglCombinerInputNV( GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_B_NV,
|
||||
GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE );
|
||||
qglCombinerOutputNV( GL_COMBINER1_NV, GL_ALPHA,
|
||||
GL_PRIMARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// final combiner
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_PRIMARY_COLOR_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_PRIMARY_COLOR_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
|
||||
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
globalImages->BindNull();
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
GL_SelectTexture( 0 );
|
||||
|
||||
RB_FinishStageTexture( &lastBumpStage->texture, surf );
|
||||
|
||||
// the bump map in the alpha channel is now corrupted, so a normal diffuse
|
||||
// map can't be drawn unless a new bumpmap is put down
|
||||
lastBumpStage = NULL;
|
||||
|
||||
// fall through to the common handling of diffuse and specular projected lighting
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// projected light / surface color for diffuse and specular maps
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
if ( surfaceStage->lighting == SL_DIFFUSE || surfaceStage->lighting == SL_SPECULAR ) {
|
||||
// don't trash alpha
|
||||
GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_ALPHAMASK | GLS_DEPTHMASK
|
||||
| backEnd.depthFunc );
|
||||
|
||||
// texture 0 will get the surface color texture
|
||||
GL_SelectTexture( 0 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
RB_BindStageTexture( surfaceRegs, &surfaceStage->texture, surf );
|
||||
|
||||
// development aid
|
||||
if ( ( surfaceStage->lighting == SL_DIFFUSE && r_skipDiffuse.GetBool() )
|
||||
|| ( surfaceStage->lighting == SL_SPECULAR && r_skipSpecular.GetBool() ) ) {
|
||||
globalImages->blackImage->Bind();
|
||||
}
|
||||
|
||||
// texture 1 will get the light projected texture
|
||||
GL_SelectTexture( 1 );
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglEnable( GL_TEXTURE_GEN_S );
|
||||
qglEnable( GL_TEXTURE_GEN_T );
|
||||
qglEnable( GL_TEXTURE_GEN_Q );
|
||||
qglTexGenfv( GL_S, GL_OBJECT_PLANE, lightProject[0].ToFloatPtr() );
|
||||
qglTexGenfv( GL_T, GL_OBJECT_PLANE, lightProject[1].ToFloatPtr() );
|
||||
qglTexGenfv( GL_Q, GL_OBJECT_PLANE, lightProject[2].ToFloatPtr() );
|
||||
|
||||
// texture0 * texture1 * primaryColor * constantColor
|
||||
qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 1 );
|
||||
|
||||
// SPARE0 = TEXTURE0 * PRIMARY_COLOR
|
||||
// SPARE1 = TEXTURE1 * CONSTANT_COLOR
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
// variable B will be overriden based on the stage vertexColor option
|
||||
if ( surfaceStage->vertexColor == SVC_MODULATE ) {
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglEnableClientState( GL_COLOR_ARRAY );
|
||||
} else if ( surfaceStage->vertexColor == SVC_INVERSE_MODULATE ) {
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_PRIMARY_COLOR_NV, GL_UNSIGNED_INVERT_NV, GL_RGB );
|
||||
qglEnableClientState( GL_COLOR_ARRAY );
|
||||
} else { // SVC_IGNORE
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB );
|
||||
qglDisableClientState( GL_COLOR_ARRAY );
|
||||
}
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV,
|
||||
GL_TEXTURE1_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV,
|
||||
GL_CONSTANT_COLOR1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
|
||||
GL_SPARE0_NV, GL_SPARE1_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// final combiner
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_SPARE1_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_SPARE0_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
|
||||
|
||||
// for all light stages, multiply the projected color by the surface
|
||||
// color, and blend with the framebuffer
|
||||
for ( int j = 0 ; j < lightShader->GetNumStages() ; j++ ) {
|
||||
const shaderStage_t *lightStage = lightShader->GetStage( j );
|
||||
float color[4];
|
||||
|
||||
// ignore stages that fail the condition
|
||||
if ( !lightRegs[ lightStage->conditionRegister ] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// set the color to the light color times the surface color
|
||||
color[0] = backEnd.lightScale
|
||||
* lightRegs[ lightStage->color.registers[0] ]
|
||||
* surfaceRegs[ surfaceStage->color.registers[0] ];
|
||||
color[1] = backEnd.lightScale
|
||||
* lightRegs[ lightStage->color.registers[1] ]
|
||||
* surfaceRegs[ surfaceStage->color.registers[1] ];
|
||||
color[2] = backEnd.lightScale
|
||||
* lightRegs[ lightStage->color.registers[2] ]
|
||||
* surfaceRegs[ surfaceStage->color.registers[2] ];
|
||||
color[3] = 1;
|
||||
|
||||
// don't draw if it would be all black
|
||||
if ( color[0] == 0 && color[1] == 0 && color[2] == 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qglCombinerParameterfvNV( GL_CONSTANT_COLOR1_NV, color );
|
||||
|
||||
RB_BindStageTexture( lightRegs, &lightStage->texture, surf );
|
||||
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
RB_FinishStageTexture( &lightStage->texture, surf );
|
||||
}
|
||||
|
||||
if ( surfaceStage->vertexColor != SVC_IGNORE ) {
|
||||
qglDisableClientState( GL_COLOR_ARRAY );
|
||||
}
|
||||
|
||||
qglDisable( GL_TEXTURE_GEN_S );
|
||||
qglDisable( GL_TEXTURE_GEN_T );
|
||||
qglDisable( GL_TEXTURE_GEN_Q );
|
||||
|
||||
globalImages->BindNull();
|
||||
GL_SelectTexture( 0 );
|
||||
RB_FinishStageTexture( &surfaceStage->texture, surf );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
// unhack depth range if needed
|
||||
if ( surf->space->weaponDepthHack || surf->space->modelDepthHack != 0.0f ) {
|
||||
RB_LeaveDepthHack();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_RenderInteractionList
|
||||
==================
|
||||
*/
|
||||
static void RB_RenderInteractionList( const drawSurf_t *surf ) {
|
||||
if ( !surf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
qglEnable( GL_REGISTER_COMBINERS_NV );
|
||||
|
||||
// force a space calculation for light vectors
|
||||
backEnd.currentSpace = NULL;
|
||||
|
||||
for ( const drawSurf_t *s = surf ; s ; s = s->nextOnLight ) {
|
||||
RB_RenderInteraction( s );
|
||||
}
|
||||
|
||||
qglDisable( GL_REGISTER_COMBINERS_NV );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_RenderViewLight
|
||||
|
||||
==================
|
||||
*/
|
||||
static void RB_RenderViewLight( viewLight_t *vLight ) {
|
||||
backEnd.vLight = vLight;
|
||||
|
||||
// do fogging later
|
||||
if ( vLight->lightShader->IsFogLight() ) {
|
||||
return;
|
||||
}
|
||||
if ( vLight->lightShader->IsBlendLight() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
RB_LogComment( "---------- RB_RenderViewLight 0x%p ----------\n", vLight );
|
||||
|
||||
// clear the stencil buffer if needed
|
||||
if ( vLight->globalShadows || vLight->localShadows ) {
|
||||
backEnd.currentScissor = vLight->scissorRect;
|
||||
if ( r_useScissor.GetBool() ) {
|
||||
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
|
||||
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
|
||||
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
|
||||
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
|
||||
}
|
||||
qglClear( GL_STENCIL_BUFFER_BIT );
|
||||
} else {
|
||||
// no shadows, so no need to read or write the stencil buffer
|
||||
// we might in theory want to use GL_ALWAYS instead of disabling
|
||||
// completely, to satisfy the invarience rules
|
||||
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
||||
}
|
||||
|
||||
backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
|
||||
RB_StencilShadowPass( vLight->globalShadows );
|
||||
RB_RenderInteractionList( vLight->localInteractions );
|
||||
RB_StencilShadowPass( vLight->localShadows );
|
||||
RB_RenderInteractionList( vLight->globalInteractions );
|
||||
|
||||
if ( r_skipTranslucent.GetBool() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// disable stencil testing for translucent interactions, because
|
||||
// the shadow isn't calculated at their point, and the shadow
|
||||
// behind them may be depth fighting with a back side, so there
|
||||
// isn't any reasonable thing to do
|
||||
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
||||
backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
|
||||
RB_RenderInteractionList( vLight->translucentInteractions );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_NV10_DrawInteractions
|
||||
==================
|
||||
*/
|
||||
void RB_NV10_DrawInteractions( void ) {
|
||||
qglEnable( GL_STENCIL_TEST );
|
||||
|
||||
for ( viewLight_t *vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
|
||||
RB_RenderViewLight( vLight );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
R_NV10_Init
|
||||
|
||||
==================
|
||||
*/
|
||||
void R_NV10_Init( void ) {
|
||||
glConfig.allowNV10Path = false;
|
||||
|
||||
if ( !glConfig.registerCombinersAvailable ) {
|
||||
return;
|
||||
}
|
||||
|
||||
glConfig.allowNV10Path = true;
|
||||
}
|
||||
885
neo/renderer/draw_nv20.cpp
Normal file
885
neo/renderer/draw_nv20.cpp
Normal file
@@ -0,0 +1,885 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
typedef enum {
|
||||
FPROG_BUMP_AND_LIGHT,
|
||||
FPROG_DIFFUSE_COLOR,
|
||||
FPROG_SPECULAR_COLOR,
|
||||
FPROG_DIFFUSE_AND_SPECULAR_COLOR,
|
||||
|
||||
FPROG_NUM_FRAGMENT_PROGRAMS
|
||||
} fragmentProgram_t;
|
||||
|
||||
GLuint fragmentDisplayListBase; // FPROG_NUM_FRAGMENT_PROGRAMS lists
|
||||
|
||||
void RB_NV20_DependentSpecularPass( const drawInteraction_t *din );
|
||||
void RB_NV20_DependentAmbientPass( void );
|
||||
|
||||
/*
|
||||
=========================================================================================
|
||||
|
||||
GENERAL INTERACTION RENDERING
|
||||
|
||||
=========================================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
====================
|
||||
GL_SelectTextureNoClient
|
||||
====================
|
||||
*/
|
||||
void GL_SelectTextureNoClient( int unit ) {
|
||||
backEnd.glState.currenttmu = unit;
|
||||
qglActiveTextureARB( GL_TEXTURE0_ARB + unit );
|
||||
RB_LogComment( "glActiveTextureARB( %i )\n", unit );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_NV20_BumpAndLightFragment
|
||||
==================
|
||||
*/
|
||||
static void RB_NV20_BumpAndLightFragment( void ) {
|
||||
if ( r_useCombinerDisplayLists.GetBool() ) {
|
||||
qglCallList( fragmentDisplayListBase + FPROG_BUMP_AND_LIGHT );
|
||||
return;
|
||||
}
|
||||
|
||||
// program the nvidia register combiners
|
||||
qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 3 );
|
||||
|
||||
// stage 0 rgb performs the dot product
|
||||
// SPARE0 = TEXTURE0 dot TEXTURE1
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
|
||||
GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE );
|
||||
|
||||
|
||||
// stage 1 rgb multiplies texture 2 and 3 together
|
||||
// SPARE1 = TEXTURE2 * TEXTURE3
|
||||
qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_TEXTURE2_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER1_NV, GL_RGB,
|
||||
GL_SPARE1_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 1 alpha does nohing
|
||||
|
||||
// stage 2 color multiplies spare0 * spare 1 just for debugging
|
||||
// SPARE0 = SPARE0 * SPARE1
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER2_NV, GL_RGB,
|
||||
GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 2 alpha multiples spare0 * spare 1
|
||||
// SPARE0 = SPARE0 * SPARE1
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_ALPHA, GL_VARIABLE_A_NV,
|
||||
GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE );
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_ALPHA, GL_VARIABLE_B_NV,
|
||||
GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE );
|
||||
qglCombinerOutputNV( GL_COMBINER2_NV, GL_ALPHA,
|
||||
GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// final combiner
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_SPARE0_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_SPARE0_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_NV20_DI_BumpAndLightPass
|
||||
|
||||
We are going to write alpha as light falloff * ( bump dot light ) * lightProjection
|
||||
If the light isn't a monoLightShader, the lightProjection will be skipped, because
|
||||
it will have to be done on an itterated basis
|
||||
==================
|
||||
*/
|
||||
static void RB_NV20_DI_BumpAndLightPass( const drawInteraction_t *din, bool monoLightShader ) {
|
||||
RB_LogComment( "---------- RB_NV_BumpAndLightPass ----------\n" );
|
||||
|
||||
GL_State( GLS_COLORMASK | GLS_DEPTHMASK | backEnd.depthFunc );
|
||||
|
||||
// texture 0 is the normalization cube map
|
||||
// GL_TEXTURE0_ARB will be the normalized vector
|
||||
// towards the light source
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 0 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 0 );
|
||||
#endif
|
||||
if ( din->ambientLight ) {
|
||||
globalImages->ambientNormalMap->Bind();
|
||||
} else {
|
||||
globalImages->normalCubeMapImage->Bind();
|
||||
}
|
||||
|
||||
// texture 1 will be the per-surface bump map
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 1 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 1 );
|
||||
#endif
|
||||
din->bumpImage->Bind();
|
||||
|
||||
// texture 2 will be the light falloff texture
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 2 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 2 );
|
||||
#endif
|
||||
din->lightFalloffImage->Bind();
|
||||
|
||||
// texture 3 will be the light projection texture
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 3 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 3 );
|
||||
#endif
|
||||
if ( monoLightShader ) {
|
||||
din->lightImage->Bind();
|
||||
} else {
|
||||
// if the projected texture is multi-colored, we
|
||||
// will need to do it in subsequent passes
|
||||
globalImages->whiteImage->Bind();
|
||||
}
|
||||
|
||||
// bind our "fragment program"
|
||||
RB_NV20_BumpAndLightFragment();
|
||||
|
||||
// draw it
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_NV20_BUMP_AND_LIGHT );
|
||||
RB_DrawElementsWithCounters( din->surf->geo );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_NV20_DiffuseColorFragment
|
||||
==================
|
||||
*/
|
||||
static void RB_NV20_DiffuseColorFragment( void ) {
|
||||
if ( r_useCombinerDisplayLists.GetBool() ) {
|
||||
qglCallList( fragmentDisplayListBase + FPROG_DIFFUSE_COLOR );
|
||||
return;
|
||||
}
|
||||
|
||||
// program the nvidia register combiners
|
||||
qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 1 );
|
||||
|
||||
// stage 0 is free, so we always do the multiply of the vertex color
|
||||
// when the vertex color is inverted, qglCombinerInputNV(GL_VARIABLE_B_NV) will be changed
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
|
||||
GL_TEXTURE0_ARB, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
qglCombinerOutputNV( GL_COMBINER0_NV, GL_ALPHA,
|
||||
GL_DISCARD_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
|
||||
// for GL_CONSTANT_COLOR0_NV * TEXTURE0 * TEXTURE1
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_CONSTANT_COLOR0_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_E_TIMES_F_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_E_NV, GL_TEXTURE0_ARB,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_F_NV, GL_TEXTURE1_ARB,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_NV20_DI_DiffuseColorPass
|
||||
|
||||
==================
|
||||
*/
|
||||
static void RB_NV20_DI_DiffuseColorPass( const drawInteraction_t *din ) {
|
||||
RB_LogComment( "---------- RB_NV20_DiffuseColorPass ----------\n" );
|
||||
|
||||
GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_ALPHAMASK
|
||||
| backEnd.depthFunc );
|
||||
|
||||
// texture 0 will be the per-surface diffuse map
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 0 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 0 );
|
||||
#endif
|
||||
din->diffuseImage->Bind();
|
||||
|
||||
// texture 1 will be the light projected texture
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 1 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 1 );
|
||||
#endif
|
||||
din->lightImage->Bind();
|
||||
|
||||
// texture 2 is disabled
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 2 );
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 2 );
|
||||
#endif
|
||||
globalImages->BindNull();
|
||||
|
||||
// texture 3 is disabled
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 3 );
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 3 );
|
||||
#endif
|
||||
globalImages->BindNull();
|
||||
|
||||
// bind our "fragment program"
|
||||
RB_NV20_DiffuseColorFragment();
|
||||
|
||||
// override one parameter for inverted vertex color
|
||||
if ( din->vertexColor == SVC_INVERSE_MODULATE ) {
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_PRIMARY_COLOR_NV, GL_UNSIGNED_INVERT_NV, GL_RGB );
|
||||
}
|
||||
|
||||
// draw it
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_NV20_DIFFUSE_COLOR );
|
||||
RB_DrawElementsWithCounters( din->surf->geo );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_NV20_SpecularColorFragment
|
||||
==================
|
||||
*/
|
||||
static void RB_NV20_SpecularColorFragment( void ) {
|
||||
if ( r_useCombinerDisplayLists.GetBool() ) {
|
||||
qglCallList( fragmentDisplayListBase + FPROG_SPECULAR_COLOR );
|
||||
return;
|
||||
}
|
||||
|
||||
// program the nvidia register combiners
|
||||
qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 4 );
|
||||
|
||||
// we want GL_CONSTANT_COLOR1_NV * PRIMARY_COLOR * TEXTURE2 * TEXTURE3 * specular( TEXTURE0 * TEXTURE1 )
|
||||
|
||||
// stage 0 rgb performs the dot product
|
||||
// GL_SPARE0_NV = ( TEXTURE0 dot TEXTURE1 - 0.5 ) * 2
|
||||
// TEXTURE2 = TEXTURE2 * PRIMARY_COLOR
|
||||
// the scale and bias steepen the specular curve
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
|
||||
GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_TRUE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 0 alpha does nothing
|
||||
|
||||
// stage 1 color takes bump * bump
|
||||
// GL_SPARE0_NV = ( GL_SPARE0_NV * GL_SPARE0_NV - 0.5 ) * 2
|
||||
// the scale and bias steepen the specular curve
|
||||
qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER1_NV, GL_RGB,
|
||||
GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 1 alpha does nothing
|
||||
|
||||
// stage 2 color
|
||||
// GL_SPARE0_NV = GL_SPARE0_NV * TEXTURE3
|
||||
// SECONDARY_COLOR = CONSTANT_COLOR * TEXTURE2
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_C_NV,
|
||||
GL_CONSTANT_COLOR1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_D_NV,
|
||||
GL_TEXTURE2_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER2_NV, GL_RGB,
|
||||
GL_SPARE0_NV, GL_SECONDARY_COLOR_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 2 alpha does nothing
|
||||
|
||||
|
||||
// stage 3 scales the texture by the vertex color
|
||||
qglCombinerInputNV( GL_COMBINER3_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_SECONDARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER3_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER3_NV, GL_RGB,
|
||||
GL_SECONDARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 3 alpha does nothing
|
||||
|
||||
// final combiner = GL_SPARE0_NV * SECONDARY_COLOR + PRIMARY_COLOR * SECONDARY_COLOR
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_SPARE0_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_SECONDARY_COLOR_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_E_TIMES_F_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_E_NV, GL_SPARE0_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_F_NV, GL_SECONDARY_COLOR_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_NV20_DI_SpecularColorPass
|
||||
|
||||
==================
|
||||
*/
|
||||
static void RB_NV20_DI_SpecularColorPass( const drawInteraction_t *din ) {
|
||||
RB_LogComment( "---------- RB_NV20_SpecularColorPass ----------\n" );
|
||||
|
||||
GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_ALPHAMASK
|
||||
| backEnd.depthFunc );
|
||||
|
||||
// texture 0 is the normalization cube map for the half angle
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 0 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 0 );
|
||||
#endif
|
||||
globalImages->normalCubeMapImage->Bind();
|
||||
|
||||
// texture 1 will be the per-surface bump map
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 1 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 1 );
|
||||
#endif
|
||||
din->bumpImage->Bind();
|
||||
|
||||
// texture 2 will be the per-surface specular map
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 2 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 2 );
|
||||
#endif
|
||||
din->specularImage->Bind();
|
||||
|
||||
// texture 3 will be the light projected texture
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 3 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 3 );
|
||||
#endif
|
||||
din->lightImage->Bind();
|
||||
|
||||
// bind our "fragment program"
|
||||
RB_NV20_SpecularColorFragment();
|
||||
|
||||
// override one parameter for inverted vertex color
|
||||
if ( din->vertexColor == SVC_INVERSE_MODULATE ) {
|
||||
qglCombinerInputNV( GL_COMBINER3_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_PRIMARY_COLOR_NV, GL_UNSIGNED_INVERT_NV, GL_RGB );
|
||||
}
|
||||
|
||||
// draw it
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_NV20_SPECULAR_COLOR );
|
||||
RB_DrawElementsWithCounters( din->surf->geo );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_NV20_DiffuseAndSpecularColorFragment
|
||||
==================
|
||||
*/
|
||||
static void RB_NV20_DiffuseAndSpecularColorFragment( void ) {
|
||||
if ( r_useCombinerDisplayLists.GetBool() ) {
|
||||
qglCallList( fragmentDisplayListBase + FPROG_DIFFUSE_AND_SPECULAR_COLOR );
|
||||
return;
|
||||
}
|
||||
|
||||
// program the nvidia register combiners
|
||||
qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 3 );
|
||||
|
||||
// GL_CONSTANT_COLOR0_NV will be the diffuse color
|
||||
// GL_CONSTANT_COLOR1_NV will be the specular color
|
||||
|
||||
// stage 0 rgb performs the dot product
|
||||
// GL_SECONDARY_COLOR_NV = ( TEXTURE0 dot TEXTURE1 - 0.5 ) * 2
|
||||
// the scale and bias steepen the specular curve
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
|
||||
GL_SECONDARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_TRUE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 0 alpha does nothing
|
||||
|
||||
// stage 1 color takes bump * bump
|
||||
// PRIMARY_COLOR = ( GL_SECONDARY_COLOR_NV * GL_SECONDARY_COLOR_NV - 0.5 ) * 2
|
||||
// the scale and bias steepen the specular curve
|
||||
qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_SECONDARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_SECONDARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER1_NV, GL_RGB,
|
||||
GL_SECONDARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
||||
GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 1 alpha does nothing
|
||||
|
||||
// stage 2 color
|
||||
// PRIMARY_COLOR = ( PRIMARY_COLOR * TEXTURE3 ) * 2
|
||||
// SPARE0 = 1.0 * 1.0 (needed for final combiner)
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_A_NV,
|
||||
GL_SECONDARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_B_NV,
|
||||
GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_C_NV,
|
||||
GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB );
|
||||
qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_D_NV,
|
||||
GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB );
|
||||
qglCombinerOutputNV( GL_COMBINER2_NV, GL_RGB,
|
||||
GL_SECONDARY_COLOR_NV, GL_SPARE0_NV, GL_DISCARD_NV,
|
||||
GL_SCALE_BY_TWO_NV, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
||||
|
||||
// stage 2 alpha does nothing
|
||||
|
||||
// final combiner = TEXTURE2_ARB * CONSTANT_COLOR0_NV + PRIMARY_COLOR_NV * CONSTANT_COLOR1_NV
|
||||
// alpha = GL_ZERO
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_CONSTANT_COLOR1_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_SECONDARY_COLOR_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_E_TIMES_F_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_E_NV, GL_TEXTURE2_ARB,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_F_NV, GL_CONSTANT_COLOR0_NV,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
||||
qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_ZERO,
|
||||
GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_NV20_DI_DiffuseAndSpecularColorPass
|
||||
|
||||
==================
|
||||
*/
|
||||
static void RB_NV20_DI_DiffuseAndSpecularColorPass( const drawInteraction_t *din ) {
|
||||
RB_LogComment( "---------- RB_NV20_DI_DiffuseAndSpecularColorPass ----------\n" );
|
||||
|
||||
GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc );
|
||||
|
||||
// texture 0 is the normalization cube map for the half angle
|
||||
// still bound from RB_NV_BumpAndLightPass
|
||||
// GL_SelectTextureNoClient( 0 );
|
||||
// GL_Bind( tr.normalCubeMapImage );
|
||||
|
||||
// texture 1 is the per-surface bump map
|
||||
// still bound from RB_NV_BumpAndLightPass
|
||||
// GL_SelectTextureNoClient( 1 );
|
||||
// GL_Bind( din->bumpImage );
|
||||
|
||||
// texture 2 is the per-surface diffuse map
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 2 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 2 );
|
||||
#endif
|
||||
din->diffuseImage->Bind();
|
||||
|
||||
// texture 3 is the per-surface specular map
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 3 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 3 );
|
||||
#endif
|
||||
din->specularImage->Bind();
|
||||
|
||||
// bind our "fragment program"
|
||||
RB_NV20_DiffuseAndSpecularColorFragment();
|
||||
|
||||
// draw it
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_NV20_DIFFUSE_AND_SPECULAR_COLOR );
|
||||
RB_DrawElementsWithCounters( din->surf->geo );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_NV20_DrawInteraction
|
||||
==================
|
||||
*/
|
||||
static void RB_NV20_DrawInteraction( const drawInteraction_t *din ) {
|
||||
const drawSurf_t *surf = din->surf;
|
||||
|
||||
// load all the vertex program parameters
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, din->localLightOrigin.ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_VIEW_ORIGIN, din->localViewOrigin.ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_S, din->lightProjection[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_T, din->lightProjection[1].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_Q, din->lightProjection[2].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_FALLOFF_S, din->lightProjection[3].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_S, din->bumpMatrix[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_T, din->bumpMatrix[1].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_S, din->diffuseMatrix[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_T, din->diffuseMatrix[1].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_S, din->specularMatrix[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_T, din->specularMatrix[1].ToFloatPtr() );
|
||||
|
||||
// set the constant colors
|
||||
qglCombinerParameterfvNV( GL_CONSTANT_COLOR0_NV, din->diffuseColor.ToFloatPtr() );
|
||||
qglCombinerParameterfvNV( GL_CONSTANT_COLOR1_NV, din->specularColor.ToFloatPtr() );
|
||||
|
||||
// vertex color passes should be pretty rare (cross-faded bump map surfaces), so always
|
||||
// run them down as three-passes
|
||||
if ( din->vertexColor != SVC_IGNORE ) {
|
||||
qglEnableClientState( GL_COLOR_ARRAY );
|
||||
RB_NV20_DI_BumpAndLightPass( din, false );
|
||||
RB_NV20_DI_DiffuseColorPass( din );
|
||||
RB_NV20_DI_SpecularColorPass( din );
|
||||
qglDisableClientState( GL_COLOR_ARRAY );
|
||||
return;
|
||||
}
|
||||
|
||||
qglColor3f( 1, 1, 1 );
|
||||
|
||||
// on an ideal card, we would now just bind the textures and call a
|
||||
// single pass vertex / fragment program, but
|
||||
// on NV20, we need to decide which single / dual / tripple pass set of programs to use
|
||||
|
||||
// ambient light could be done as a single pass if we want to optimize for it
|
||||
|
||||
// monochrome light is two passes
|
||||
int internalFormat = din->lightImage->internalFormat;
|
||||
if ( ( r_useNV20MonoLights.GetInteger() == 2 ) ||
|
||||
( din->lightImage->isMonochrome && r_useNV20MonoLights.GetInteger() ) ) {
|
||||
// do a two-pass rendering
|
||||
RB_NV20_DI_BumpAndLightPass( din, true );
|
||||
RB_NV20_DI_DiffuseAndSpecularColorPass( din );
|
||||
} else {
|
||||
// general case is three passes
|
||||
// ( bump dot lightDir ) * lightFalloff
|
||||
// diffuse * lightProject
|
||||
// specular * ( bump dot halfAngle extended ) * lightProject
|
||||
RB_NV20_DI_BumpAndLightPass( din, false );
|
||||
RB_NV20_DI_DiffuseColorPass( din );
|
||||
RB_NV20_DI_SpecularColorPass( din );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
RB_NV20_CreateDrawInteractions
|
||||
|
||||
=============
|
||||
*/
|
||||
static void RB_NV20_CreateDrawInteractions( const drawSurf_t *surf ) {
|
||||
if ( !surf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
qglEnable( GL_VERTEX_PROGRAM_ARB );
|
||||
qglEnable( GL_REGISTER_COMBINERS_NV );
|
||||
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture(0);
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
qglEnableVertexAttribArrayARB( 8 );
|
||||
qglEnableVertexAttribArrayARB( 9 );
|
||||
qglEnableVertexAttribArrayARB( 10 );
|
||||
qglEnableVertexAttribArrayARB( 11 );
|
||||
#endif
|
||||
|
||||
for ( ; surf ; surf=surf->nextOnLight ) {
|
||||
// set the vertex pointers
|
||||
idDrawVert *ac = (idDrawVert *)vertexCache.Position( surf->geo->ambientCache );
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), ac->color );
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 0 );
|
||||
qglTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
|
||||
GL_SelectTexture( 1 );
|
||||
qglTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() );
|
||||
GL_SelectTexture( 2 );
|
||||
qglTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() );
|
||||
GL_SelectTexture( 3 );
|
||||
qglTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->normal.ToFloatPtr() );
|
||||
GL_SelectTexture( 0 );
|
||||
#else
|
||||
qglVertexAttribPointerARB( 11, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->normal.ToFloatPtr() );
|
||||
qglVertexAttribPointerARB( 10, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() );
|
||||
qglVertexAttribPointerARB( 9, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() );
|
||||
qglVertexAttribPointerARB( 8, 2, GL_FLOAT, false, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
|
||||
#endif
|
||||
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
|
||||
|
||||
RB_CreateSingleDrawInteractions( surf, RB_NV20_DrawInteraction );
|
||||
}
|
||||
|
||||
#ifndef MACOS_X
|
||||
qglDisableVertexAttribArrayARB( 8 );
|
||||
qglDisableVertexAttribArrayARB( 9 );
|
||||
qglDisableVertexAttribArrayARB( 10 );
|
||||
qglDisableVertexAttribArrayARB( 11 );
|
||||
#endif
|
||||
|
||||
// disable features
|
||||
#ifdef MACOS_X
|
||||
GL_SelectTexture( 3 );
|
||||
globalImages->BindNull();
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
GL_SelectTexture( 2 );
|
||||
globalImages->BindNull();
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
GL_SelectTexture( 1 );
|
||||
globalImages->BindNull();
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
#else
|
||||
GL_SelectTextureNoClient( 3 );
|
||||
globalImages->BindNull();
|
||||
|
||||
GL_SelectTextureNoClient( 2 );
|
||||
globalImages->BindNull();
|
||||
|
||||
GL_SelectTextureNoClient( 1 );
|
||||
globalImages->BindNull();
|
||||
#endif
|
||||
|
||||
backEnd.glState.currenttmu = -1;
|
||||
GL_SelectTexture( 0 );
|
||||
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
qglDisable( GL_VERTEX_PROGRAM_ARB );
|
||||
qglDisable( GL_REGISTER_COMBINERS_NV );
|
||||
}
|
||||
|
||||
|
||||
//======================================================================================
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_NV20_DrawInteractions
|
||||
==================
|
||||
*/
|
||||
void RB_NV20_DrawInteractions( void ) {
|
||||
viewLight_t *vLight;
|
||||
|
||||
//
|
||||
// for each light, perform adding and shadowing
|
||||
//
|
||||
for ( vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
|
||||
// do fogging later
|
||||
if ( vLight->lightShader->IsFogLight() ) {
|
||||
continue;
|
||||
}
|
||||
if ( vLight->lightShader->IsBlendLight() ) {
|
||||
continue;
|
||||
}
|
||||
if ( !vLight->localInteractions && !vLight->globalInteractions
|
||||
&& !vLight->translucentInteractions ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
backEnd.vLight = vLight;
|
||||
|
||||
RB_LogComment( "---------- RB_RenderViewLight 0x%p ----------\n", vLight );
|
||||
|
||||
// clear the stencil buffer if needed
|
||||
if ( vLight->globalShadows || vLight->localShadows ) {
|
||||
backEnd.currentScissor = vLight->scissorRect;
|
||||
if ( r_useScissor.GetBool() ) {
|
||||
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
|
||||
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
|
||||
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
|
||||
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
|
||||
}
|
||||
qglClear( GL_STENCIL_BUFFER_BIT );
|
||||
} else {
|
||||
// no shadows, so no need to read or write the stencil buffer
|
||||
// we might in theory want to use GL_ALWAYS instead of disabling
|
||||
// completely, to satisfy the invarience rules
|
||||
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
||||
}
|
||||
|
||||
backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
|
||||
|
||||
if ( r_useShadowVertexProgram.GetBool() ) {
|
||||
qglEnable( GL_VERTEX_PROGRAM_ARB );
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
|
||||
RB_StencilShadowPass( vLight->globalShadows );
|
||||
RB_NV20_CreateDrawInteractions( vLight->localInteractions );
|
||||
qglEnable( GL_VERTEX_PROGRAM_ARB );
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
|
||||
RB_StencilShadowPass( vLight->localShadows );
|
||||
RB_NV20_CreateDrawInteractions( vLight->globalInteractions );
|
||||
qglDisable( GL_VERTEX_PROGRAM_ARB ); // if there weren't any globalInteractions, it would have stayed on
|
||||
} else {
|
||||
RB_StencilShadowPass( vLight->globalShadows );
|
||||
RB_NV20_CreateDrawInteractions( vLight->localInteractions );
|
||||
RB_StencilShadowPass( vLight->localShadows );
|
||||
RB_NV20_CreateDrawInteractions( vLight->globalInteractions );
|
||||
}
|
||||
|
||||
// translucent surfaces never get stencil shadowed
|
||||
if ( r_skipTranslucent.GetBool() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
||||
|
||||
backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
|
||||
RB_NV20_CreateDrawInteractions( vLight->translucentInteractions );
|
||||
|
||||
backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
|
||||
}
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
R_NV20_Init
|
||||
|
||||
==================
|
||||
*/
|
||||
void R_NV20_Init( void ) {
|
||||
glConfig.allowNV20Path = false;
|
||||
|
||||
common->Printf( "---------- R_NV20_Init ----------\n" );
|
||||
|
||||
if ( !glConfig.registerCombinersAvailable || !glConfig.ARBVertexProgramAvailable || glConfig.maxTextureUnits < 4 ) {
|
||||
common->Printf( "Not available.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
GL_CheckErrors();
|
||||
|
||||
// create our "fragment program" display lists
|
||||
fragmentDisplayListBase = qglGenLists( FPROG_NUM_FRAGMENT_PROGRAMS );
|
||||
|
||||
// force them to issue commands to build the list
|
||||
bool temp = r_useCombinerDisplayLists.GetBool();
|
||||
r_useCombinerDisplayLists.SetBool( false );
|
||||
|
||||
qglNewList( fragmentDisplayListBase + FPROG_BUMP_AND_LIGHT, GL_COMPILE );
|
||||
RB_NV20_BumpAndLightFragment();
|
||||
qglEndList();
|
||||
|
||||
qglNewList( fragmentDisplayListBase + FPROG_DIFFUSE_COLOR, GL_COMPILE );
|
||||
RB_NV20_DiffuseColorFragment();
|
||||
qglEndList();
|
||||
|
||||
qglNewList( fragmentDisplayListBase + FPROG_SPECULAR_COLOR, GL_COMPILE );
|
||||
RB_NV20_SpecularColorFragment();
|
||||
qglEndList();
|
||||
|
||||
qglNewList( fragmentDisplayListBase + FPROG_DIFFUSE_AND_SPECULAR_COLOR, GL_COMPILE );
|
||||
RB_NV20_DiffuseAndSpecularColorFragment();
|
||||
qglEndList();
|
||||
|
||||
r_useCombinerDisplayLists.SetBool( temp );
|
||||
|
||||
common->Printf( "---------------------------------\n" );
|
||||
|
||||
glConfig.allowNV20Path = true;
|
||||
}
|
||||
|
||||
516
neo/renderer/draw_r200.cpp
Normal file
516
neo/renderer/draw_r200.cpp
Normal file
@@ -0,0 +1,516 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "tr_local.h"
|
||||
|
||||
/*
|
||||
|
||||
There are not enough vertex program texture coordinate outputs
|
||||
to have unique texture coordinates for bump, specular, and diffuse,
|
||||
so diffuse and specular are assumed to be the same mapping.
|
||||
|
||||
To handle properly, those cases with different diffuse and specular
|
||||
mapping will need to be run as two passes.
|
||||
|
||||
*/
|
||||
|
||||
// changed from 1 to 255 to not conflict with ARB2 program names
|
||||
static int FPROG_FAST_PATH = 255;
|
||||
|
||||
typedef struct {
|
||||
GLint numFragmentRegisters; // 6
|
||||
GLint numFragmentConstants; // 8
|
||||
GLint numPasses; // 2
|
||||
GLint numInstructionsPerPass; // 8
|
||||
GLint numInstructionsTotal; // 16
|
||||
GLint colorAlphaPairing; // 1
|
||||
GLint numLoopbackComponenets; // 3
|
||||
GLint numInputInterpolatorComponents; // 3
|
||||
} atiFragmentShaderInfo_t;
|
||||
|
||||
static atiFragmentShaderInfo_t fsi;
|
||||
|
||||
typedef struct {
|
||||
// vertex shader invariants
|
||||
int lightPos; // light position in object coordinates
|
||||
int viewerPos; // viewer position in object coordinates
|
||||
int lightProjectS; // projected light s texgen
|
||||
int lightProjectT; // projected light t texgen
|
||||
int lightProjectQ; // projected light q texgen
|
||||
int lightFalloffS; // projected light falloff s texgen
|
||||
int bumpTransformS; // bump TEX0 S transformation
|
||||
int bumpTransformT; // bump TEX0 T transformation
|
||||
int colorTransformS; // diffuse/specular texture matrix
|
||||
int colorTransformT; // diffuse/specular texture matrix
|
||||
|
||||
// vertex shader variants
|
||||
int texCoords;
|
||||
int vertexColors;
|
||||
int normals;
|
||||
int tangents;
|
||||
int biTangents;
|
||||
|
||||
} atiVertexShaderInfo_t;
|
||||
|
||||
static atiVertexShaderInfo_t vsi;
|
||||
|
||||
/*
|
||||
===================
|
||||
RB_R200_ARB_DrawInteraction
|
||||
|
||||
===================
|
||||
*/
|
||||
static void RB_R200_ARB_DrawInteraction( const drawInteraction_t *din ) {
|
||||
// check for the case we can't handle in a single pass (we could calculate this at shader parse time to optimize)
|
||||
if ( din->diffuseImage != globalImages->blackImage && din->specularImage != globalImages->blackImage
|
||||
&& memcmp( din->specularMatrix, din->diffuseMatrix, sizeof( din->diffuseMatrix ) ) ) {
|
||||
// common->Printf( "Note: Shader %s drawn as two pass on R200\n", din->surf->shader->getName() );
|
||||
|
||||
// draw the specular as a separate pass with a black diffuse map
|
||||
drawInteraction_t d;
|
||||
d = *din;
|
||||
d.diffuseImage = globalImages->blackImage;
|
||||
memcpy( d.diffuseMatrix, d.specularMatrix, sizeof( d.diffuseMatrix ) );
|
||||
RB_R200_ARB_DrawInteraction( &d );
|
||||
|
||||
// now fall through and draw the diffuse pass with a black specular map
|
||||
d = *din;
|
||||
din = &d;
|
||||
d.specularImage = globalImages->blackImage;
|
||||
}
|
||||
|
||||
// load all the vertex program parameters
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, din->localLightOrigin.ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_VIEW_ORIGIN, din->localViewOrigin.ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_S, din->lightProjection[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_T, din->lightProjection[1].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_Q, din->lightProjection[2].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_FALLOFF_S, din->lightProjection[3].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_S, din->bumpMatrix[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_T, din->bumpMatrix[1].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_S, din->diffuseMatrix[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_T, din->diffuseMatrix[1].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_S, din->diffuseMatrix[0].ToFloatPtr() );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_T, din->diffuseMatrix[1].ToFloatPtr() );
|
||||
|
||||
const srfTriangles_t *tri = din->surf->geo;
|
||||
idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache );
|
||||
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), (void *)&ac->xyz );
|
||||
|
||||
static const float zero[4] = { 0, 0, 0, 0 };
|
||||
static const float one[4] = { 1, 1, 1, 1 };
|
||||
static const float negOne[4] = { -1, -1, -1, -1 };
|
||||
|
||||
switch ( din->vertexColor ) {
|
||||
case SVC_IGNORE:
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, zero );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
|
||||
break;
|
||||
case SVC_MODULATE:
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, one );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, zero );
|
||||
break;
|
||||
case SVC_INVERSE_MODULATE:
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, negOne );
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// texture 0 = light projection
|
||||
// texture 1 = light falloff
|
||||
// texture 2 = surface diffuse
|
||||
// texture 3 = surface specular
|
||||
// texture 4 = surface bump
|
||||
// texture 5 = normalization cube map
|
||||
|
||||
GL_SelectTexture( 5 );
|
||||
if ( din->ambientLight ) {
|
||||
globalImages->ambientNormalMap->Bind();
|
||||
} else {
|
||||
globalImages->normalCubeMapImage->Bind();
|
||||
}
|
||||
|
||||
GL_SelectTexture( 4 );
|
||||
din->bumpImage->Bind();
|
||||
|
||||
GL_SelectTexture( 3 );
|
||||
din->specularImage->Bind();
|
||||
qglTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), (void *)&ac->normal );
|
||||
|
||||
GL_SelectTexture( 2 );
|
||||
din->diffuseImage->Bind();
|
||||
qglTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), (void *)&ac->tangents[1][0] );
|
||||
|
||||
GL_SelectTexture( 1 );
|
||||
din->lightFalloffImage->Bind();
|
||||
qglTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), (void *)&ac->tangents[0][0] );
|
||||
|
||||
GL_SelectTexture( 0 );
|
||||
din->lightImage->Bind();
|
||||
qglTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), (void *)&ac->st[0] );
|
||||
|
||||
qglSetFragmentShaderConstantATI( GL_CON_0_ATI, din->diffuseColor.ToFloatPtr() );
|
||||
qglSetFragmentShaderConstantATI( GL_CON_1_ATI, din->specularColor.ToFloatPtr() );
|
||||
|
||||
if ( din->vertexColor != SVC_IGNORE ) {
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof(idDrawVert), (void *)&ac->color );
|
||||
qglEnableClientState( GL_COLOR_ARRAY );
|
||||
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
qglDisableClientState( GL_COLOR_ARRAY );
|
||||
qglColor4f( 1, 1, 1, 1 );
|
||||
} else {
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_R200_ARB_CreateDrawInteractions
|
||||
==================
|
||||
*/
|
||||
static void RB_R200_ARB_CreateDrawInteractions( const drawSurf_t *surf ) {
|
||||
if ( !surf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// force a space calculation for light vectors
|
||||
backEnd.currentSpace = NULL;
|
||||
|
||||
// set the depth test
|
||||
if ( surf->material->Coverage() == MC_TRANSLUCENT /* != C_PERFORATED */ ) {
|
||||
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_DEPTHFUNC_LESS );
|
||||
} else {
|
||||
// only draw on the alpha tested pixels that made it to the depth buffer
|
||||
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL );
|
||||
}
|
||||
|
||||
// start the vertex shader
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_R200_INTERACTION );
|
||||
qglEnable(GL_VERTEX_PROGRAM_ARB);
|
||||
|
||||
// start the fragment shader
|
||||
qglBindFragmentShaderATI( FPROG_FAST_PATH );
|
||||
#if defined( MACOS_X )
|
||||
qglEnable( GL_TEXT_FRAGMENT_SHADER_ATI );
|
||||
#else
|
||||
qglEnable( GL_FRAGMENT_SHADER_ATI );
|
||||
#endif
|
||||
|
||||
qglColor4f( 1, 1, 1, 1 );
|
||||
|
||||
GL_SelectTexture( 1 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
GL_SelectTexture( 2 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
GL_SelectTexture( 3 );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
for ( ; surf ; surf=surf->nextOnLight ) {
|
||||
RB_CreateSingleDrawInteractions( surf, RB_R200_ARB_DrawInteraction );
|
||||
}
|
||||
|
||||
GL_SelectTexture( 5 );
|
||||
globalImages->BindNull();
|
||||
|
||||
GL_SelectTexture( 4 );
|
||||
globalImages->BindNull();
|
||||
|
||||
GL_SelectTexture( 3 );
|
||||
globalImages->BindNull();
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
GL_SelectTexture( 2 );
|
||||
globalImages->BindNull();
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
GL_SelectTexture( 1 );
|
||||
globalImages->BindNull();
|
||||
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
GL_SelectTexture( 0 );
|
||||
|
||||
qglDisable( GL_VERTEX_PROGRAM_ARB );
|
||||
#if defined( MACOS_X )
|
||||
qglDisable( GL_TEXT_FRAGMENT_SHADER_ATI );
|
||||
#else
|
||||
qglDisable( GL_FRAGMENT_SHADER_ATI );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
RB_R200_DrawInteractions
|
||||
|
||||
==================
|
||||
*/
|
||||
void RB_R200_DrawInteractions( void ) {
|
||||
qglEnable( GL_STENCIL_TEST );
|
||||
|
||||
for ( viewLight_t *vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
|
||||
// do fogging later
|
||||
if ( vLight->lightShader->IsFogLight() ) {
|
||||
continue;
|
||||
}
|
||||
if ( vLight->lightShader->IsBlendLight() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
backEnd.vLight = vLight;
|
||||
|
||||
RB_LogComment( "---------- RB_RenderViewLight 0x%p ----------\n", vLight );
|
||||
|
||||
// clear the stencil buffer if needed
|
||||
if ( vLight->globalShadows || vLight->localShadows ) {
|
||||
backEnd.currentScissor = vLight->scissorRect;
|
||||
if ( r_useScissor.GetBool() ) {
|
||||
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
|
||||
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
|
||||
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
|
||||
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
|
||||
}
|
||||
qglClear( GL_STENCIL_BUFFER_BIT );
|
||||
} else {
|
||||
// no shadows, so no need to read or write the stencil buffer
|
||||
// we might in theory want to use GL_ALWAYS instead of disabling
|
||||
// completely, to satisfy the invarience rules
|
||||
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
||||
}
|
||||
|
||||
if ( r_useShadowVertexProgram.GetBool() ) {
|
||||
qglEnable( GL_VERTEX_PROGRAM_ARB );
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
|
||||
RB_StencilShadowPass( vLight->globalShadows );
|
||||
|
||||
RB_R200_ARB_CreateDrawInteractions( vLight->localInteractions );
|
||||
|
||||
qglEnable( GL_VERTEX_PROGRAM_ARB );
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
|
||||
RB_StencilShadowPass( vLight->localShadows );
|
||||
|
||||
RB_R200_ARB_CreateDrawInteractions( vLight->globalInteractions );
|
||||
|
||||
qglDisable( GL_VERTEX_PROGRAM_ARB ); // if there weren't any globalInteractions, it would have stayed on
|
||||
} else {
|
||||
RB_StencilShadowPass( vLight->globalShadows );
|
||||
RB_R200_ARB_CreateDrawInteractions( vLight->localInteractions );
|
||||
|
||||
RB_StencilShadowPass( vLight->localShadows );
|
||||
RB_R200_ARB_CreateDrawInteractions( vLight->globalInteractions );
|
||||
}
|
||||
|
||||
if ( r_skipTranslucent.GetBool() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// disable stencil testing for translucent interactions, because
|
||||
// the shadow isn't calculated at their point, and the shadow
|
||||
// behind them may be depth fighting with a back side, so there
|
||||
// isn't any reasonable thing to do
|
||||
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
||||
RB_R200_ARB_CreateDrawInteractions( vLight->translucentInteractions );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
R_BuildSurfaceFragmentProgram
|
||||
=================
|
||||
*/
|
||||
static void R_BuildSurfaceFragmentProgram( int programNum ) {
|
||||
qglBindFragmentShaderATI( programNum );
|
||||
|
||||
qglBeginFragmentShaderATI();
|
||||
|
||||
// texture 0 = light projection
|
||||
// texture 1 = light falloff
|
||||
// texture 2 = surface diffuse
|
||||
// texture 3 = surface specular
|
||||
// texture 4 = surface bump
|
||||
// texture 5 = normalization cube map
|
||||
|
||||
// texcoord 0 = light projection texGen
|
||||
// texcoord 1 = light falloff texGen
|
||||
// texcoord 2 = bumpmap texCoords
|
||||
// texcoord 3 = specular / diffuse texCoords
|
||||
// texcoord 4 = halfangle vector in local tangent space
|
||||
// texcoord 5 = vector to light in local tangent space
|
||||
|
||||
// constant 0 = diffuse modulate
|
||||
// constant 1 = specular modulate
|
||||
// constant 5 = internal use for 0.75 constant
|
||||
|
||||
qglSampleMapATI( GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STQ_DQ_ATI );
|
||||
qglSampleMapATI( GL_REG_1_ATI, GL_TEXTURE1_ARB, GL_SWIZZLE_STR_ATI );
|
||||
qglSampleMapATI( GL_REG_4_ATI, GL_TEXTURE2_ARB, GL_SWIZZLE_STR_ATI );
|
||||
qglSampleMapATI( GL_REG_5_ATI, GL_TEXTURE5_ARB, GL_SWIZZLE_STR_ATI );
|
||||
|
||||
// move the alpha component to the red channel to support rxgb normal map compression
|
||||
if ( globalImages->image_useNormalCompression.GetInteger() == 2 ) {
|
||||
qglColorFragmentOp1ATI( GL_MOV_ATI, GL_REG_4_ATI, GL_RED_BIT_ATI, GL_NONE,
|
||||
GL_REG_4_ATI, GL_ALPHA, GL_NONE );
|
||||
}
|
||||
|
||||
// light projection * light falloff
|
||||
qglColorFragmentOp2ATI( GL_MUL_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE,
|
||||
GL_REG_0_ATI, GL_NONE, GL_NONE,
|
||||
GL_REG_1_ATI, GL_NONE, GL_NONE );
|
||||
|
||||
// vectorToLight dot bumpMap
|
||||
qglColorFragmentOp2ATI( GL_DOT3_ATI, GL_REG_1_ATI, GL_NONE, GL_SATURATE_BIT_ATI,
|
||||
GL_REG_4_ATI, GL_NONE, GL_2X_BIT_ATI | GL_BIAS_BIT_ATI,
|
||||
GL_REG_5_ATI, GL_NONE, GL_2X_BIT_ATI | GL_BIAS_BIT_ATI );
|
||||
|
||||
// bump * light
|
||||
qglColorFragmentOp2ATI( GL_MUL_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE,
|
||||
GL_REG_0_ATI, GL_NONE, GL_NONE,
|
||||
GL_REG_1_ATI, GL_NONE, GL_NONE );
|
||||
|
||||
//-------------------
|
||||
|
||||
// carry over the incomingLight calculation
|
||||
qglPassTexCoordATI( GL_REG_0_ATI, GL_REG_0_ATI, GL_SWIZZLE_STR_ATI );
|
||||
|
||||
// sample the diffuse surface map
|
||||
qglSampleMapATI( GL_REG_2_ATI, GL_TEXTURE3_ARB, GL_SWIZZLE_STR_ATI );
|
||||
|
||||
// sample the specular surface map
|
||||
qglSampleMapATI( GL_REG_3_ATI, GL_TEXTURE3_ARB, GL_SWIZZLE_STR_ATI );
|
||||
|
||||
// we will use the surface bump map again
|
||||
qglPassTexCoordATI( GL_REG_4_ATI, GL_REG_4_ATI, GL_SWIZZLE_STR_ATI );
|
||||
|
||||
// normalize the specular halfangle
|
||||
qglSampleMapATI( GL_REG_5_ATI, GL_TEXTURE4_ARB, GL_SWIZZLE_STR_ATI );
|
||||
|
||||
// R1 = halfangle dot surfaceNormal
|
||||
qglColorFragmentOp2ATI( GL_DOT3_ATI, GL_REG_1_ATI, GL_NONE, GL_SATURATE_BIT_ATI,
|
||||
GL_REG_4_ATI, GL_NONE, GL_2X_BIT_ATI | GL_BIAS_BIT_ATI,
|
||||
GL_REG_5_ATI, GL_NONE, GL_2X_BIT_ATI | GL_BIAS_BIT_ATI );
|
||||
|
||||
// R1 = 4 * ( R1 - 0.75 )
|
||||
// subtract 0.75 and quadruple to tighten the specular spot
|
||||
float data[4] = { 0.75, 0.75, 0.75, 0.75 };
|
||||
qglSetFragmentShaderConstantATI( GL_CON_5_ATI, data );
|
||||
qglColorFragmentOp2ATI( GL_SUB_ATI, GL_REG_1_ATI, GL_NONE, GL_4X_BIT_ATI | GL_SATURATE_BIT_ATI,
|
||||
GL_REG_1_ATI, GL_NONE, GL_NONE,
|
||||
GL_CON_5_ATI, GL_NONE, GL_NONE );
|
||||
|
||||
// R1 = R1 * R1
|
||||
// sqare the stretched specular result
|
||||
qglColorFragmentOp2ATI( GL_MUL_ATI, GL_REG_1_ATI, GL_NONE, GL_SATURATE_BIT_ATI,
|
||||
GL_REG_1_ATI, GL_NONE, GL_NONE,
|
||||
GL_REG_1_ATI, GL_NONE, GL_NONE );
|
||||
|
||||
// R1 = R1 * R3
|
||||
// R1 = specular power * specular texture * 2
|
||||
qglColorFragmentOp2ATI( GL_MUL_ATI, GL_REG_1_ATI, GL_NONE, GL_2X_BIT_ATI | GL_SATURATE_BIT_ATI,
|
||||
GL_REG_1_ATI, GL_NONE, GL_NONE,
|
||||
GL_REG_3_ATI, GL_NONE, GL_NONE );
|
||||
|
||||
// R2 = R2 * CONST0
|
||||
// down modulate the diffuse map
|
||||
qglColorFragmentOp2ATI( GL_MUL_ATI, GL_REG_2_ATI, GL_NONE, GL_SATURATE_BIT_ATI,
|
||||
GL_REG_2_ATI, GL_NONE, GL_NONE,
|
||||
GL_CON_0_ATI, GL_NONE, GL_NONE );
|
||||
|
||||
// R2 = R2 + R1 * CONST1
|
||||
// diffuse + specular * specular color
|
||||
qglColorFragmentOp3ATI( GL_MAD_ATI, GL_REG_2_ATI, GL_NONE, GL_SATURATE_BIT_ATI,
|
||||
GL_REG_1_ATI, GL_NONE, GL_NONE,
|
||||
GL_CON_1_ATI, GL_NONE, GL_NONE,
|
||||
GL_REG_2_ATI, GL_NONE, GL_NONE );
|
||||
|
||||
// out = reflectance * incoming light
|
||||
qglColorFragmentOp2ATI( GL_MUL_ATI, GL_REG_0_ATI, GL_NONE, GL_SATURATE_BIT_ATI,
|
||||
GL_REG_0_ATI, GL_NONE, GL_NONE,
|
||||
GL_REG_2_ATI, GL_NONE, GL_NONE );
|
||||
|
||||
// out * vertex color
|
||||
qglColorFragmentOp2ATI( GL_MUL_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE,
|
||||
GL_REG_0_ATI, GL_NONE, GL_NONE,
|
||||
GL_PRIMARY_COLOR_ARB, GL_NONE, GL_NONE );
|
||||
|
||||
// out alpha = 0 to allow blending optimization
|
||||
qglAlphaFragmentOp1ATI( GL_MOV_ATI, GL_REG_0_ATI, GL_NONE,
|
||||
GL_ZERO, GL_NONE, GL_NONE );
|
||||
|
||||
qglEndFragmentShaderATI();
|
||||
|
||||
GL_CheckErrors();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_R200_Init
|
||||
=================
|
||||
*/
|
||||
void R_R200_Init( void ) {
|
||||
glConfig.allowR200Path = false;
|
||||
|
||||
common->Printf( "----------- R200_Init -----------\n" );
|
||||
|
||||
if ( !glConfig.atiFragmentShaderAvailable || !glConfig.ARBVertexProgramAvailable || !glConfig.ARBVertexBufferObjectAvailable ) {
|
||||
common->Printf( "Not available.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
GL_CheckErrors();
|
||||
|
||||
qglGetIntegerv( GL_NUM_FRAGMENT_REGISTERS_ATI, &fsi.numFragmentRegisters );
|
||||
qglGetIntegerv( GL_NUM_FRAGMENT_CONSTANTS_ATI, &fsi.numFragmentConstants );
|
||||
qglGetIntegerv( GL_NUM_PASSES_ATI, &fsi.numPasses );
|
||||
qglGetIntegerv( GL_NUM_INSTRUCTIONS_PER_PASS_ATI, &fsi.numInstructionsPerPass );
|
||||
qglGetIntegerv( GL_NUM_INSTRUCTIONS_TOTAL_ATI, &fsi.numInstructionsTotal );
|
||||
qglGetIntegerv( GL_COLOR_ALPHA_PAIRING_ATI, &fsi.colorAlphaPairing );
|
||||
qglGetIntegerv( GL_NUM_LOOPBACK_COMPONENTS_ATI, &fsi.numLoopbackComponenets );
|
||||
qglGetIntegerv( GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI, &fsi.numInputInterpolatorComponents );
|
||||
|
||||
common->Printf( "GL_NUM_FRAGMENT_REGISTERS_ATI: %i\n", fsi.numFragmentRegisters );
|
||||
common->Printf( "GL_NUM_FRAGMENT_CONSTANTS_ATI: %i\n", fsi.numFragmentConstants );
|
||||
common->Printf( "GL_NUM_PASSES_ATI: %i\n", fsi.numPasses );
|
||||
common->Printf( "GL_NUM_INSTRUCTIONS_PER_PASS_ATI: %i\n", fsi.numInstructionsPerPass );
|
||||
common->Printf( "GL_NUM_INSTRUCTIONS_TOTAL_ATI: %i\n", fsi.numInstructionsTotal );
|
||||
common->Printf( "GL_COLOR_ALPHA_PAIRING_ATI: %i\n", fsi.colorAlphaPairing );
|
||||
common->Printf( "GL_NUM_LOOPBACK_COMPONENTS_ATI: %i\n", fsi.numLoopbackComponenets );
|
||||
common->Printf( "GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI: %i\n", fsi.numInputInterpolatorComponents );
|
||||
|
||||
common->Printf( "FPROG_FAST_PATH\n" );
|
||||
R_BuildSurfaceFragmentProgram( FPROG_FAST_PATH );
|
||||
|
||||
common->Printf( "---------------------\n" );
|
||||
|
||||
glConfig.allowR200Path = true;
|
||||
}
|
||||
5919
neo/renderer/glext.h
Normal file
5919
neo/renderer/glext.h
Normal file
File diff suppressed because it is too large
Load Diff
228
neo/renderer/jpeg-6/jcapimin.c
Normal file
228
neo/renderer/jpeg-6/jcapimin.c
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* jcapimin.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains application interface code for the compression half
|
||||
* of the JPEG library. These are the "minimum" API routines that may be
|
||||
* needed in either the normal full-compression case or the transcoding-only
|
||||
* case.
|
||||
*
|
||||
* Most of the routines intended to be called directly by an application
|
||||
* are in this file or in jcapistd.c. But also see jcparam.c for
|
||||
* parameter-setup helper routines, jcomapi.c for routines shared by
|
||||
* compression and decompression, and jctrans.c for the transcoding case.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/*
|
||||
* Initialization of a JPEG compression object.
|
||||
* The error manager must already be set up (in case memory manager fails).
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_create_compress (j_compress_ptr cinfo)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* For debugging purposes, zero the whole master structure.
|
||||
* But error manager pointer is already there, so save and restore it.
|
||||
*/
|
||||
{
|
||||
struct jpeg_error_mgr * err = cinfo->err;
|
||||
MEMZERO(cinfo, SIZEOF(struct jpeg_compress_struct));
|
||||
cinfo->err = err;
|
||||
}
|
||||
cinfo->is_decompressor = FALSE;
|
||||
|
||||
/* Initialize a memory manager instance for this object */
|
||||
jinit_memory_mgr((j_common_ptr) cinfo);
|
||||
|
||||
/* Zero out pointers to permanent structures. */
|
||||
cinfo->progress = NULL;
|
||||
cinfo->dest = NULL;
|
||||
|
||||
cinfo->comp_info = NULL;
|
||||
|
||||
for (i = 0; i < NUM_QUANT_TBLS; i++)
|
||||
cinfo->quant_tbl_ptrs[i] = NULL;
|
||||
|
||||
for (i = 0; i < NUM_HUFF_TBLS; i++) {
|
||||
cinfo->dc_huff_tbl_ptrs[i] = NULL;
|
||||
cinfo->ac_huff_tbl_ptrs[i] = NULL;
|
||||
}
|
||||
|
||||
cinfo->input_gamma = 1.0; /* in case application forgets */
|
||||
|
||||
/* OK, I'm ready */
|
||||
cinfo->global_state = CSTATE_START;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Destruction of a JPEG compression object
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_destroy_compress (j_compress_ptr cinfo)
|
||||
{
|
||||
jpeg_destroy((j_common_ptr) cinfo); /* use common routine */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Abort processing of a JPEG compression operation,
|
||||
* but don't destroy the object itself.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_abort_compress (j_compress_ptr cinfo)
|
||||
{
|
||||
jpeg_abort((j_common_ptr) cinfo); /* use common routine */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Forcibly suppress or un-suppress all quantization and Huffman tables.
|
||||
* Marks all currently defined tables as already written (if suppress)
|
||||
* or not written (if !suppress). This will control whether they get emitted
|
||||
* by a subsequent jpeg_start_compress call.
|
||||
*
|
||||
* This routine is exported for use by applications that want to produce
|
||||
* abbreviated JPEG datastreams. It logically belongs in jcparam.c, but
|
||||
* since it is called by jpeg_start_compress, we put it here --- otherwise
|
||||
* jcparam.o would be linked whether the application used it or not.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_suppress_tables (j_compress_ptr cinfo, boolean suppress)
|
||||
{
|
||||
int i;
|
||||
JQUANT_TBL * qtbl;
|
||||
JHUFF_TBL * htbl;
|
||||
|
||||
for (i = 0; i < NUM_QUANT_TBLS; i++) {
|
||||
if ((qtbl = cinfo->quant_tbl_ptrs[i]) != NULL)
|
||||
qtbl->sent_table = suppress;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_HUFF_TBLS; i++) {
|
||||
if ((htbl = cinfo->dc_huff_tbl_ptrs[i]) != NULL)
|
||||
htbl->sent_table = suppress;
|
||||
if ((htbl = cinfo->ac_huff_tbl_ptrs[i]) != NULL)
|
||||
htbl->sent_table = suppress;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish JPEG compression.
|
||||
*
|
||||
* If a multipass operating mode was selected, this may do a great deal of
|
||||
* work including most of the actual output.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_finish_compress (j_compress_ptr cinfo)
|
||||
{
|
||||
JDIMENSION iMCU_row;
|
||||
|
||||
if (cinfo->global_state == CSTATE_SCANNING ||
|
||||
cinfo->global_state == CSTATE_RAW_OK) {
|
||||
/* Terminate first pass */
|
||||
if (cinfo->next_scanline < cinfo->image_height)
|
||||
ERREXIT(cinfo, JERR_TOO_LITTLE_DATA);
|
||||
(*cinfo->master->finish_pass) (cinfo);
|
||||
} else if (cinfo->global_state != CSTATE_WRCOEFS)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
/* Perform any remaining passes */
|
||||
while (! cinfo->master->is_last_pass) {
|
||||
(*cinfo->master->prepare_for_pass) (cinfo);
|
||||
for (iMCU_row = 0; iMCU_row < cinfo->total_iMCU_rows; iMCU_row++) {
|
||||
if (cinfo->progress != NULL) {
|
||||
cinfo->progress->pass_counter = (long) iMCU_row;
|
||||
cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows;
|
||||
(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
|
||||
}
|
||||
/* We bypass the main controller and invoke coef controller directly;
|
||||
* all work is being done from the coefficient buffer.
|
||||
*/
|
||||
if (! (*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE) NULL))
|
||||
ERREXIT(cinfo, JERR_CANT_SUSPEND);
|
||||
}
|
||||
(*cinfo->master->finish_pass) (cinfo);
|
||||
}
|
||||
/* Write EOI, do final cleanup */
|
||||
(*cinfo->marker->write_file_trailer) (cinfo);
|
||||
(*cinfo->dest->term_destination) (cinfo);
|
||||
/* We can use jpeg_abort to release memory and reset global_state */
|
||||
jpeg_abort((j_common_ptr) cinfo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write a special marker.
|
||||
* This is only recommended for writing COM or APPn markers.
|
||||
* Must be called after jpeg_start_compress() and before
|
||||
* first call to jpeg_write_scanlines() or jpeg_write_raw_data().
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_write_marker (j_compress_ptr cinfo, int marker,
|
||||
const JOCTET *dataptr, unsigned int datalen)
|
||||
{
|
||||
if (cinfo->next_scanline != 0 ||
|
||||
(cinfo->global_state != CSTATE_SCANNING &&
|
||||
cinfo->global_state != CSTATE_RAW_OK &&
|
||||
cinfo->global_state != CSTATE_WRCOEFS))
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
|
||||
(*cinfo->marker->write_any_marker) (cinfo, marker, dataptr, datalen);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Alternate compression function: just write an abbreviated table file.
|
||||
* Before calling this, all parameters and a data destination must be set up.
|
||||
*
|
||||
* To produce a pair of files containing abbreviated tables and abbreviated
|
||||
* image data, one would proceed as follows:
|
||||
*
|
||||
* initialize JPEG object
|
||||
* set JPEG parameters
|
||||
* set destination to table file
|
||||
* jpeg_write_tables(cinfo);
|
||||
* set destination to image file
|
||||
* jpeg_start_compress(cinfo, FALSE);
|
||||
* write data...
|
||||
* jpeg_finish_compress(cinfo);
|
||||
*
|
||||
* jpeg_write_tables has the side effect of marking all tables written
|
||||
* (same as jpeg_suppress_tables(..., TRUE)). Thus a subsequent start_compress
|
||||
* will not re-emit the tables unless it is passed write_all_tables=TRUE.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_write_tables (j_compress_ptr cinfo)
|
||||
{
|
||||
if (cinfo->global_state != CSTATE_START)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
|
||||
/* (Re)initialize error mgr and destination modules */
|
||||
(*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);
|
||||
(*cinfo->dest->init_destination) (cinfo);
|
||||
/* Initialize the marker writer ... bit of a crock to do it here. */
|
||||
jinit_marker_writer(cinfo);
|
||||
/* Write them tables! */
|
||||
(*cinfo->marker->write_tables_only) (cinfo);
|
||||
/* And clean up. */
|
||||
(*cinfo->dest->term_destination) (cinfo);
|
||||
/* We can use jpeg_abort to release memory. */
|
||||
jpeg_abort((j_common_ptr) cinfo);
|
||||
}
|
||||
161
neo/renderer/jpeg-6/jcapistd.c
Normal file
161
neo/renderer/jpeg-6/jcapistd.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* jcapistd.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains application interface code for the compression half
|
||||
* of the JPEG library. These are the "standard" API routines that are
|
||||
* used in the normal full-compression case. They are not used by a
|
||||
* transcoding-only application. Note that if an application links in
|
||||
* jpeg_start_compress, it will end up linking in the entire compressor.
|
||||
* We thus must separate this file from jcapimin.c to avoid linking the
|
||||
* whole compression library into a transcoder.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/*
|
||||
* Compression initialization.
|
||||
* Before calling this, all parameters and a data destination must be set up.
|
||||
*
|
||||
* We require a write_all_tables parameter as a failsafe check when writing
|
||||
* multiple datastreams from the same compression object. Since prior runs
|
||||
* will have left all the tables marked sent_table=TRUE, a subsequent run
|
||||
* would emit an abbreviated stream (no tables) by default. This may be what
|
||||
* is wanted, but for safety's sake it should not be the default behavior:
|
||||
* programmers should have to make a deliberate choice to emit abbreviated
|
||||
* images. Therefore the documentation and examples should encourage people
|
||||
* to pass write_all_tables=TRUE; then it will take active thought to do the
|
||||
* wrong thing.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_start_compress (j_compress_ptr cinfo, boolean write_all_tables)
|
||||
{
|
||||
if (cinfo->global_state != CSTATE_START)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
|
||||
if (write_all_tables)
|
||||
jpeg_suppress_tables(cinfo, FALSE); /* mark all tables to be written */
|
||||
|
||||
/* (Re)initialize error mgr and destination modules */
|
||||
(*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);
|
||||
(*cinfo->dest->init_destination) (cinfo);
|
||||
/* Perform master selection of active modules */
|
||||
jinit_compress_master(cinfo);
|
||||
/* Set up for the first pass */
|
||||
(*cinfo->master->prepare_for_pass) (cinfo);
|
||||
/* Ready for application to drive first pass through jpeg_write_scanlines
|
||||
* or jpeg_write_raw_data.
|
||||
*/
|
||||
cinfo->next_scanline = 0;
|
||||
cinfo->global_state = (cinfo->raw_data_in ? CSTATE_RAW_OK : CSTATE_SCANNING);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write some scanlines of data to the JPEG compressor.
|
||||
*
|
||||
* The return value will be the number of lines actually written.
|
||||
* This should be less than the supplied num_lines only in case that
|
||||
* the data destination module has requested suspension of the compressor,
|
||||
* or if more than image_height scanlines are passed in.
|
||||
*
|
||||
* Note: we warn about excess calls to jpeg_write_scanlines() since
|
||||
* this likely signals an application programmer error. However,
|
||||
* excess scanlines passed in the last valid call are *silently* ignored,
|
||||
* so that the application need not adjust num_lines for end-of-image
|
||||
* when using a multiple-scanline buffer.
|
||||
*/
|
||||
|
||||
GLOBAL JDIMENSION
|
||||
jpeg_write_scanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines,
|
||||
JDIMENSION num_lines)
|
||||
{
|
||||
JDIMENSION row_ctr, rows_left;
|
||||
|
||||
if (cinfo->global_state != CSTATE_SCANNING)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
if (cinfo->next_scanline >= cinfo->image_height)
|
||||
WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
|
||||
|
||||
/* Call progress monitor hook if present */
|
||||
if (cinfo->progress != NULL) {
|
||||
cinfo->progress->pass_counter = (long) cinfo->next_scanline;
|
||||
cinfo->progress->pass_limit = (long) cinfo->image_height;
|
||||
(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
|
||||
}
|
||||
|
||||
/* Give master control module another chance if this is first call to
|
||||
* jpeg_write_scanlines. This lets output of the frame/scan headers be
|
||||
* delayed so that application can write COM, etc, markers between
|
||||
* jpeg_start_compress and jpeg_write_scanlines.
|
||||
*/
|
||||
if (cinfo->master->call_pass_startup)
|
||||
(*cinfo->master->pass_startup) (cinfo);
|
||||
|
||||
/* Ignore any extra scanlines at bottom of image. */
|
||||
rows_left = cinfo->image_height - cinfo->next_scanline;
|
||||
if (num_lines > rows_left)
|
||||
num_lines = rows_left;
|
||||
|
||||
row_ctr = 0;
|
||||
(*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, num_lines);
|
||||
cinfo->next_scanline += row_ctr;
|
||||
return row_ctr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Alternate entry point to write raw data.
|
||||
* Processes exactly one iMCU row per call, unless suspended.
|
||||
*/
|
||||
|
||||
GLOBAL JDIMENSION
|
||||
jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data,
|
||||
JDIMENSION num_lines)
|
||||
{
|
||||
JDIMENSION lines_per_iMCU_row;
|
||||
|
||||
if (cinfo->global_state != CSTATE_RAW_OK)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
if (cinfo->next_scanline >= cinfo->image_height) {
|
||||
WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call progress monitor hook if present */
|
||||
if (cinfo->progress != NULL) {
|
||||
cinfo->progress->pass_counter = (long) cinfo->next_scanline;
|
||||
cinfo->progress->pass_limit = (long) cinfo->image_height;
|
||||
(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
|
||||
}
|
||||
|
||||
/* Give master control module another chance if this is first call to
|
||||
* jpeg_write_raw_data. This lets output of the frame/scan headers be
|
||||
* delayed so that application can write COM, etc, markers between
|
||||
* jpeg_start_compress and jpeg_write_raw_data.
|
||||
*/
|
||||
if (cinfo->master->call_pass_startup)
|
||||
(*cinfo->master->pass_startup) (cinfo);
|
||||
|
||||
/* Verify that at least one iMCU row has been passed. */
|
||||
lines_per_iMCU_row = cinfo->max_v_samp_factor * DCTSIZE;
|
||||
if (num_lines < lines_per_iMCU_row)
|
||||
ERREXIT(cinfo, JERR_BUFFER_SIZE);
|
||||
|
||||
/* Directly compress the row. */
|
||||
if (! (*cinfo->coef->compress_data) (cinfo, data)) {
|
||||
/* If compressor did not consume the whole row, suspend processing. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* OK, we processed one iMCU row. */
|
||||
cinfo->next_scanline += lines_per_iMCU_row;
|
||||
return lines_per_iMCU_row;
|
||||
}
|
||||
448
neo/renderer/jpeg-6/jccoefct.c
Normal file
448
neo/renderer/jpeg-6/jccoefct.c
Normal file
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
* jccoefct.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains the coefficient buffer controller for compression.
|
||||
* This controller is the top level of the JPEG compressor proper.
|
||||
* The coefficient buffer lies between forward-DCT and entropy encoding steps.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* We use a full-image coefficient buffer when doing Huffman optimization,
|
||||
* and also for writing multiple-scan JPEG files. In all cases, the DCT
|
||||
* step is run during the first pass, and subsequent passes need only read
|
||||
* the buffered coefficients.
|
||||
*/
|
||||
#ifdef ENTROPY_OPT_SUPPORTED
|
||||
#define FULL_COEF_BUFFER_SUPPORTED
|
||||
#else
|
||||
#ifdef C_MULTISCAN_FILES_SUPPORTED
|
||||
#define FULL_COEF_BUFFER_SUPPORTED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* Private buffer controller object */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_c_coef_controller pub; /* public fields */
|
||||
|
||||
JDIMENSION iMCU_row_num; /* iMCU row # within image */
|
||||
JDIMENSION mcu_ctr; /* counts MCUs processed in current row */
|
||||
int MCU_vert_offset; /* counts MCU rows within iMCU row */
|
||||
int MCU_rows_per_iMCU_row; /* number of such rows needed */
|
||||
|
||||
/* For single-pass compression, it's sufficient to buffer just one MCU
|
||||
* (although this may prove a bit slow in practice). We allocate a
|
||||
* workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each
|
||||
* MCU constructed and sent. (On 80x86, the workspace is FAR even though
|
||||
* it's not really very big; this is to keep the module interfaces unchanged
|
||||
* when a large coefficient buffer is necessary.)
|
||||
* In multi-pass modes, this array points to the current MCU's blocks
|
||||
* within the virtual arrays.
|
||||
*/
|
||||
JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU];
|
||||
|
||||
/* In multi-pass modes, we need a virtual block array for each component. */
|
||||
jvirt_barray_ptr whole_image[MAX_COMPONENTS];
|
||||
} my_coef_controller;
|
||||
|
||||
typedef my_coef_controller * my_coef_ptr;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
METHODDEF boolean compress_data
|
||||
JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf));
|
||||
#ifdef FULL_COEF_BUFFER_SUPPORTED
|
||||
METHODDEF boolean compress_first_pass
|
||||
JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf));
|
||||
METHODDEF boolean compress_output
|
||||
JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf));
|
||||
#endif
|
||||
|
||||
|
||||
LOCAL void
|
||||
start_iMCU_row (j_compress_ptr cinfo)
|
||||
/* Reset within-iMCU-row counters for a new row */
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
|
||||
/* In an interleaved scan, an MCU row is the same as an iMCU row.
|
||||
* In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows.
|
||||
* But at the bottom of the image, process only what's left.
|
||||
*/
|
||||
if (cinfo->comps_in_scan > 1) {
|
||||
coef->MCU_rows_per_iMCU_row = 1;
|
||||
} else {
|
||||
if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1))
|
||||
coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor;
|
||||
else
|
||||
coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height;
|
||||
}
|
||||
|
||||
coef->mcu_ctr = 0;
|
||||
coef->MCU_vert_offset = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a processing pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode)
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
|
||||
coef->iMCU_row_num = 0;
|
||||
start_iMCU_row(cinfo);
|
||||
|
||||
switch (pass_mode) {
|
||||
case JBUF_PASS_THRU:
|
||||
if (coef->whole_image[0] != NULL)
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
coef->pub.compress_data = compress_data;
|
||||
break;
|
||||
#ifdef FULL_COEF_BUFFER_SUPPORTED
|
||||
case JBUF_SAVE_AND_PASS:
|
||||
if (coef->whole_image[0] == NULL)
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
coef->pub.compress_data = compress_first_pass;
|
||||
break;
|
||||
case JBUF_CRANK_DEST:
|
||||
if (coef->whole_image[0] == NULL)
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
coef->pub.compress_data = compress_output;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process some data in the single-pass case.
|
||||
* We process the equivalent of one fully interleaved MCU row ("iMCU" row)
|
||||
* per call, ie, v_samp_factor block rows for each component in the image.
|
||||
* Returns TRUE if the iMCU row is completed, FALSE if suspended.
|
||||
*
|
||||
* NB: input_buf contains a plane for each component in image.
|
||||
* For single pass, this is the same as the components in the scan.
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
JDIMENSION MCU_col_num; /* index of current MCU within row */
|
||||
JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1;
|
||||
JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
|
||||
int blkn, bi, ci, yindex, yoffset, blockcnt;
|
||||
JDIMENSION ypos, xpos;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
/* Loop to write as much as one whole iMCU row */
|
||||
for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row;
|
||||
yoffset++) {
|
||||
for (MCU_col_num = coef->mcu_ctr; MCU_col_num <= last_MCU_col;
|
||||
MCU_col_num++) {
|
||||
/* Determine where data comes from in input_buf and do the DCT thing.
|
||||
* Each call on forward_DCT processes a horizontal row of DCT blocks
|
||||
* as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks
|
||||
* sequentially. Dummy blocks at the right or bottom edge are filled in
|
||||
* specially. The data in them does not matter for image reconstruction,
|
||||
* so we fill them with values that will encode to the smallest amount of
|
||||
* data, viz: all zeroes in the AC entries, DC entries equal to previous
|
||||
* block's DC value. (Thanks to Thomas Kinsman for this idea.)
|
||||
*/
|
||||
blkn = 0;
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width
|
||||
: compptr->last_col_width;
|
||||
xpos = MCU_col_num * compptr->MCU_sample_width;
|
||||
ypos = yoffset * DCTSIZE; /* ypos == (yoffset+yindex) * DCTSIZE */
|
||||
for (yindex = 0; yindex < compptr->MCU_height; yindex++) {
|
||||
if (coef->iMCU_row_num < last_iMCU_row ||
|
||||
yoffset+yindex < compptr->last_row_height) {
|
||||
(*cinfo->fdct->forward_DCT) (cinfo, compptr,
|
||||
input_buf[ci], coef->MCU_buffer[blkn],
|
||||
ypos, xpos, (JDIMENSION) blockcnt);
|
||||
if (blockcnt < compptr->MCU_width) {
|
||||
/* Create some dummy blocks at the right edge of the image. */
|
||||
jzero_far((void FAR *) coef->MCU_buffer[blkn + blockcnt],
|
||||
(compptr->MCU_width - blockcnt) * SIZEOF(JBLOCK));
|
||||
for (bi = blockcnt; bi < compptr->MCU_width; bi++) {
|
||||
coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn+bi-1][0][0];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Create a row of dummy blocks at the bottom of the image. */
|
||||
jzero_far((void FAR *) coef->MCU_buffer[blkn],
|
||||
compptr->MCU_width * SIZEOF(JBLOCK));
|
||||
for (bi = 0; bi < compptr->MCU_width; bi++) {
|
||||
coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn-1][0][0];
|
||||
}
|
||||
}
|
||||
blkn += compptr->MCU_width;
|
||||
ypos += DCTSIZE;
|
||||
}
|
||||
}
|
||||
/* Try to write the MCU. In event of a suspension failure, we will
|
||||
* re-DCT the MCU on restart (a bit inefficient, could be fixed...)
|
||||
*/
|
||||
if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) {
|
||||
/* Suspension forced; update state counters and exit */
|
||||
coef->MCU_vert_offset = yoffset;
|
||||
coef->mcu_ctr = MCU_col_num;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
/* Completed an MCU row, but perhaps not an iMCU row */
|
||||
coef->mcu_ctr = 0;
|
||||
}
|
||||
/* Completed the iMCU row, advance counters for next one */
|
||||
coef->iMCU_row_num++;
|
||||
start_iMCU_row(cinfo);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
#ifdef FULL_COEF_BUFFER_SUPPORTED
|
||||
|
||||
/*
|
||||
* Process some data in the first pass of a multi-pass case.
|
||||
* We process the equivalent of one fully interleaved MCU row ("iMCU" row)
|
||||
* per call, ie, v_samp_factor block rows for each component in the image.
|
||||
* This amount of data is read from the source buffer, DCT'd and quantized,
|
||||
* and saved into the virtual arrays. We also generate suitable dummy blocks
|
||||
* as needed at the right and lower edges. (The dummy blocks are constructed
|
||||
* in the virtual arrays, which have been padded appropriately.) This makes
|
||||
* it possible for subsequent passes not to worry about real vs. dummy blocks.
|
||||
*
|
||||
* We must also emit the data to the entropy encoder. This is conveniently
|
||||
* done by calling compress_output() after we've loaded the current strip
|
||||
* of the virtual arrays.
|
||||
*
|
||||
* NB: input_buf contains a plane for each component in image. All
|
||||
* components are DCT'd and loaded into the virtual arrays in this pass.
|
||||
* However, it may be that only a subset of the components are emitted to
|
||||
* the entropy encoder during this first pass; be careful about looking
|
||||
* at the scan-dependent variables (MCU dimensions, etc).
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
|
||||
JDIMENSION blocks_across, MCUs_across, MCUindex;
|
||||
int bi, ci, h_samp_factor, block_row, block_rows, ndummy;
|
||||
JCOEF lastDC;
|
||||
jpeg_component_info *compptr;
|
||||
JBLOCKARRAY buffer;
|
||||
JBLOCKROW thisblockrow, lastblockrow;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* Align the virtual buffer for this component. */
|
||||
buffer = (*cinfo->mem->access_virt_barray)
|
||||
((j_common_ptr) cinfo, coef->whole_image[ci],
|
||||
coef->iMCU_row_num * compptr->v_samp_factor,
|
||||
(JDIMENSION) compptr->v_samp_factor, TRUE);
|
||||
/* Count non-dummy DCT block rows in this iMCU row. */
|
||||
if (coef->iMCU_row_num < last_iMCU_row)
|
||||
block_rows = compptr->v_samp_factor;
|
||||
else {
|
||||
/* NB: can't use last_row_height here, since may not be set! */
|
||||
block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor);
|
||||
if (block_rows == 0) block_rows = compptr->v_samp_factor;
|
||||
}
|
||||
blocks_across = compptr->width_in_blocks;
|
||||
h_samp_factor = compptr->h_samp_factor;
|
||||
/* Count number of dummy blocks to be added at the right margin. */
|
||||
ndummy = (int) (blocks_across % h_samp_factor);
|
||||
if (ndummy > 0)
|
||||
ndummy = h_samp_factor - ndummy;
|
||||
/* Perform DCT for all non-dummy blocks in this iMCU row. Each call
|
||||
* on forward_DCT processes a complete horizontal row of DCT blocks.
|
||||
*/
|
||||
for (block_row = 0; block_row < block_rows; block_row++) {
|
||||
thisblockrow = buffer[block_row];
|
||||
(*cinfo->fdct->forward_DCT) (cinfo, compptr,
|
||||
input_buf[ci], thisblockrow,
|
||||
(JDIMENSION) (block_row * DCTSIZE),
|
||||
(JDIMENSION) 0, blocks_across);
|
||||
if (ndummy > 0) {
|
||||
/* Create dummy blocks at the right edge of the image. */
|
||||
thisblockrow += blocks_across; /* => first dummy block */
|
||||
jzero_far((void FAR *) thisblockrow, ndummy * SIZEOF(JBLOCK));
|
||||
lastDC = thisblockrow[-1][0];
|
||||
for (bi = 0; bi < ndummy; bi++) {
|
||||
thisblockrow[bi][0] = lastDC;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If at end of image, create dummy block rows as needed.
|
||||
* The tricky part here is that within each MCU, we want the DC values
|
||||
* of the dummy blocks to match the last real block's DC value.
|
||||
* This squeezes a few more bytes out of the resulting file...
|
||||
*/
|
||||
if (coef->iMCU_row_num == last_iMCU_row) {
|
||||
blocks_across += ndummy; /* include lower right corner */
|
||||
MCUs_across = blocks_across / h_samp_factor;
|
||||
for (block_row = block_rows; block_row < compptr->v_samp_factor;
|
||||
block_row++) {
|
||||
thisblockrow = buffer[block_row];
|
||||
lastblockrow = buffer[block_row-1];
|
||||
jzero_far((void FAR *) thisblockrow,
|
||||
(size_t) (blocks_across * SIZEOF(JBLOCK)));
|
||||
for (MCUindex = 0; MCUindex < MCUs_across; MCUindex++) {
|
||||
lastDC = lastblockrow[h_samp_factor-1][0];
|
||||
for (bi = 0; bi < h_samp_factor; bi++) {
|
||||
thisblockrow[bi][0] = lastDC;
|
||||
}
|
||||
thisblockrow += h_samp_factor; /* advance to next MCU in row */
|
||||
lastblockrow += h_samp_factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* NB: compress_output will increment iMCU_row_num if successful.
|
||||
* A suspension return will result in redoing all the work above next time.
|
||||
*/
|
||||
|
||||
/* Emit data to the entropy encoder, sharing code with subsequent passes */
|
||||
return compress_output(cinfo, input_buf);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process some data in subsequent passes of a multi-pass case.
|
||||
* We process the equivalent of one fully interleaved MCU row ("iMCU" row)
|
||||
* per call, ie, v_samp_factor block rows for each component in the scan.
|
||||
* The data is obtained from the virtual arrays and fed to the entropy coder.
|
||||
* Returns TRUE if the iMCU row is completed, FALSE if suspended.
|
||||
*
|
||||
* NB: input_buf is ignored; it is likely to be a NULL pointer.
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
JDIMENSION MCU_col_num; /* index of current MCU within row */
|
||||
int blkn, ci, xindex, yindex, yoffset;
|
||||
JDIMENSION start_col;
|
||||
JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN];
|
||||
JBLOCKROW buffer_ptr;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
/* Align the virtual buffers for the components used in this scan.
|
||||
* NB: during first pass, this is safe only because the buffers will
|
||||
* already be aligned properly, so jmemmgr.c won't need to do any I/O.
|
||||
*/
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
buffer[ci] = (*cinfo->mem->access_virt_barray)
|
||||
((j_common_ptr) cinfo, coef->whole_image[compptr->component_index],
|
||||
coef->iMCU_row_num * compptr->v_samp_factor,
|
||||
(JDIMENSION) compptr->v_samp_factor, FALSE);
|
||||
}
|
||||
|
||||
/* Loop to process one whole iMCU row */
|
||||
for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row;
|
||||
yoffset++) {
|
||||
for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row;
|
||||
MCU_col_num++) {
|
||||
/* Construct list of pointers to DCT blocks belonging to this MCU */
|
||||
blkn = 0; /* index of current DCT block within MCU */
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
start_col = MCU_col_num * compptr->MCU_width;
|
||||
for (yindex = 0; yindex < compptr->MCU_height; yindex++) {
|
||||
buffer_ptr = buffer[ci][yindex+yoffset] + start_col;
|
||||
for (xindex = 0; xindex < compptr->MCU_width; xindex++) {
|
||||
coef->MCU_buffer[blkn++] = buffer_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Try to write the MCU. */
|
||||
if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) {
|
||||
/* Suspension forced; update state counters and exit */
|
||||
coef->MCU_vert_offset = yoffset;
|
||||
coef->mcu_ctr = MCU_col_num;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
/* Completed an MCU row, but perhaps not an iMCU row */
|
||||
coef->mcu_ctr = 0;
|
||||
}
|
||||
/* Completed the iMCU row, advance counters for next one */
|
||||
coef->iMCU_row_num++;
|
||||
start_iMCU_row(cinfo);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif /* FULL_COEF_BUFFER_SUPPORTED */
|
||||
|
||||
|
||||
/*
|
||||
* Initialize coefficient buffer controller.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer)
|
||||
{
|
||||
my_coef_ptr coef;
|
||||
|
||||
coef = (my_coef_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_coef_controller));
|
||||
cinfo->coef = (struct jpeg_c_coef_controller *) coef;
|
||||
coef->pub.start_pass = start_pass_coef;
|
||||
|
||||
/* Create the coefficient buffer. */
|
||||
if (need_full_buffer) {
|
||||
#ifdef FULL_COEF_BUFFER_SUPPORTED
|
||||
/* Allocate a full-image virtual array for each component, */
|
||||
/* padded to a multiple of samp_factor DCT blocks in each direction. */
|
||||
int ci;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
coef->whole_image[ci] = (*cinfo->mem->request_virt_barray)
|
||||
((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
|
||||
(JDIMENSION) jround_up((long) compptr->width_in_blocks,
|
||||
(long) compptr->h_samp_factor),
|
||||
(JDIMENSION) jround_up((long) compptr->height_in_blocks,
|
||||
(long) compptr->v_samp_factor),
|
||||
(JDIMENSION) compptr->v_samp_factor);
|
||||
}
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
#endif
|
||||
} else {
|
||||
/* We only need a single-MCU buffer. */
|
||||
JBLOCKROW buffer;
|
||||
int i;
|
||||
|
||||
buffer = (JBLOCKROW)
|
||||
(*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK));
|
||||
for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) {
|
||||
coef->MCU_buffer[i] = buffer + i;
|
||||
}
|
||||
coef->whole_image[0] = NULL; /* flag for no virtual arrays */
|
||||
}
|
||||
}
|
||||
459
neo/renderer/jpeg-6/jccolor.c
Normal file
459
neo/renderer/jpeg-6/jccolor.c
Normal file
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* jccolor.c
|
||||
*
|
||||
* Copyright (C) 1991-1994, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains input colorspace conversion routines.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Private subobject */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_color_converter pub; /* public fields */
|
||||
|
||||
/* Private state for RGB->YCC conversion */
|
||||
INT32 * rgb_ycc_tab; /* => table for RGB to YCbCr conversion */
|
||||
} my_color_converter;
|
||||
|
||||
typedef my_color_converter * my_cconvert_ptr;
|
||||
|
||||
|
||||
/**************** RGB -> YCbCr conversion: most common case **************/
|
||||
|
||||
/*
|
||||
* YCbCr is defined per CCIR 601-1, except that Cb and Cr are
|
||||
* normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
|
||||
* The conversion equations to be implemented are therefore
|
||||
* Y = 0.29900 * R + 0.58700 * G + 0.11400 * B
|
||||
* Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTERJSAMPLE
|
||||
* Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTERJSAMPLE
|
||||
* (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.)
|
||||
* Note: older versions of the IJG code used a zero offset of MAXJSAMPLE/2,
|
||||
* rather than CENTERJSAMPLE, for Cb and Cr. This gave equal positive and
|
||||
* negative swings for Cb/Cr, but meant that grayscale values (Cb=Cr=0)
|
||||
* were not represented exactly. Now we sacrifice exact representation of
|
||||
* maximum red and maximum blue in order to get exact grayscales.
|
||||
*
|
||||
* To avoid floating-point arithmetic, we represent the fractional constants
|
||||
* as integers scaled up by 2^16 (about 4 digits precision); we have to divide
|
||||
* the products by 2^16, with appropriate rounding, to get the correct answer.
|
||||
*
|
||||
* For even more speed, we avoid doing any multiplications in the inner loop
|
||||
* by precalculating the constants times R,G,B for all possible values.
|
||||
* For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table);
|
||||
* for 12-bit samples it is still acceptable. It's not very reasonable for
|
||||
* 16-bit samples, but if you want lossless storage you shouldn't be changing
|
||||
* colorspace anyway.
|
||||
* The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included
|
||||
* in the tables to save adding them separately in the inner loop.
|
||||
*/
|
||||
|
||||
#define SCALEBITS 16 /* speediest right-shift on some machines */
|
||||
#define CBCR_OFFSET ((INT32) CENTERJSAMPLE << SCALEBITS)
|
||||
#define ONE_HALF ((INT32) 1 << (SCALEBITS-1))
|
||||
#define FIX(x) ((INT32) ((x) * (1L<<SCALEBITS) + 0.5))
|
||||
|
||||
/* We allocate one big table and divide it up into eight parts, instead of
|
||||
* doing eight alloc_small requests. This lets us use a single table base
|
||||
* address, which can be held in a register in the inner loops on many
|
||||
* machines (more than can hold all eight addresses, anyway).
|
||||
*/
|
||||
|
||||
#define R_Y_OFF 0 /* offset to R => Y section */
|
||||
#define G_Y_OFF (1*(MAXJSAMPLE+1)) /* offset to G => Y section */
|
||||
#define B_Y_OFF (2*(MAXJSAMPLE+1)) /* etc. */
|
||||
#define R_CB_OFF (3*(MAXJSAMPLE+1))
|
||||
#define G_CB_OFF (4*(MAXJSAMPLE+1))
|
||||
#define B_CB_OFF (5*(MAXJSAMPLE+1))
|
||||
#define R_CR_OFF B_CB_OFF /* B=>Cb, R=>Cr are the same */
|
||||
#define G_CR_OFF (6*(MAXJSAMPLE+1))
|
||||
#define B_CR_OFF (7*(MAXJSAMPLE+1))
|
||||
#define TABLE_SIZE (8*(MAXJSAMPLE+1))
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for RGB->YCC colorspace conversion.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
rgb_ycc_start (j_compress_ptr cinfo)
|
||||
{
|
||||
my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
|
||||
INT32 * rgb_ycc_tab;
|
||||
INT32 i;
|
||||
|
||||
/* Allocate and fill in the conversion tables. */
|
||||
cconvert->rgb_ycc_tab = rgb_ycc_tab = (INT32 *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(TABLE_SIZE * SIZEOF(INT32)));
|
||||
|
||||
for (i = 0; i <= MAXJSAMPLE; i++) {
|
||||
rgb_ycc_tab[i+R_Y_OFF] = FIX(0.29900) * i;
|
||||
rgb_ycc_tab[i+G_Y_OFF] = FIX(0.58700) * i;
|
||||
rgb_ycc_tab[i+B_Y_OFF] = FIX(0.11400) * i + ONE_HALF;
|
||||
rgb_ycc_tab[i+R_CB_OFF] = (-FIX(0.16874)) * i;
|
||||
rgb_ycc_tab[i+G_CB_OFF] = (-FIX(0.33126)) * i;
|
||||
/* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr.
|
||||
* This ensures that the maximum output will round to MAXJSAMPLE
|
||||
* not MAXJSAMPLE+1, and thus that we don't have to range-limit.
|
||||
*/
|
||||
rgb_ycc_tab[i+B_CB_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1;
|
||||
/* B=>Cb and R=>Cr tables are the same
|
||||
rgb_ycc_tab[i+R_CR_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1;
|
||||
*/
|
||||
rgb_ycc_tab[i+G_CR_OFF] = (-FIX(0.41869)) * i;
|
||||
rgb_ycc_tab[i+B_CR_OFF] = (-FIX(0.08131)) * i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert some rows of samples to the JPEG colorspace.
|
||||
*
|
||||
* Note that we change from the application's interleaved-pixel format
|
||||
* to our internal noninterleaved, one-plane-per-component format.
|
||||
* The input buffer is therefore three times as wide as the output buffer.
|
||||
*
|
||||
* A starting row offset is provided only for the output buffer. The caller
|
||||
* can easily adjust the passed input_buf value to accommodate any row
|
||||
* offset required on that side.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
rgb_ycc_convert (j_compress_ptr cinfo,
|
||||
JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
|
||||
JDIMENSION output_row, int num_rows)
|
||||
{
|
||||
my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
|
||||
register int r, g, b;
|
||||
register INT32 * ctab = cconvert->rgb_ycc_tab;
|
||||
register JSAMPROW inptr;
|
||||
register JSAMPROW outptr0, outptr1, outptr2;
|
||||
register JDIMENSION col;
|
||||
JDIMENSION num_cols = cinfo->image_width;
|
||||
|
||||
while (--num_rows >= 0) {
|
||||
inptr = *input_buf++;
|
||||
outptr0 = output_buf[0][output_row];
|
||||
outptr1 = output_buf[1][output_row];
|
||||
outptr2 = output_buf[2][output_row];
|
||||
output_row++;
|
||||
for (col = 0; col < num_cols; col++) {
|
||||
r = GETJSAMPLE(inptr[RGB_RED]);
|
||||
g = GETJSAMPLE(inptr[RGB_GREEN]);
|
||||
b = GETJSAMPLE(inptr[RGB_BLUE]);
|
||||
inptr += RGB_PIXELSIZE;
|
||||
/* If the inputs are 0..MAXJSAMPLE, the outputs of these equations
|
||||
* must be too; we do not need an explicit range-limiting operation.
|
||||
* Hence the value being shifted is never negative, and we don't
|
||||
* need the general RIGHT_SHIFT macro.
|
||||
*/
|
||||
/* Y */
|
||||
outptr0[col] = (JSAMPLE)
|
||||
((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF])
|
||||
>> SCALEBITS);
|
||||
/* Cb */
|
||||
outptr1[col] = (JSAMPLE)
|
||||
((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF])
|
||||
>> SCALEBITS);
|
||||
/* Cr */
|
||||
outptr2[col] = (JSAMPLE)
|
||||
((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF])
|
||||
>> SCALEBITS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************** Cases other than RGB -> YCbCr **************/
|
||||
|
||||
|
||||
/*
|
||||
* Convert some rows of samples to the JPEG colorspace.
|
||||
* This version handles RGB->grayscale conversion, which is the same
|
||||
* as the RGB->Y portion of RGB->YCbCr.
|
||||
* We assume rgb_ycc_start has been called (we only use the Y tables).
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
rgb_gray_convert (j_compress_ptr cinfo,
|
||||
JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
|
||||
JDIMENSION output_row, int num_rows)
|
||||
{
|
||||
my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
|
||||
register int r, g, b;
|
||||
register INT32 * ctab = cconvert->rgb_ycc_tab;
|
||||
register JSAMPROW inptr;
|
||||
register JSAMPROW outptr;
|
||||
register JDIMENSION col;
|
||||
JDIMENSION num_cols = cinfo->image_width;
|
||||
|
||||
while (--num_rows >= 0) {
|
||||
inptr = *input_buf++;
|
||||
outptr = output_buf[0][output_row];
|
||||
output_row++;
|
||||
for (col = 0; col < num_cols; col++) {
|
||||
r = GETJSAMPLE(inptr[RGB_RED]);
|
||||
g = GETJSAMPLE(inptr[RGB_GREEN]);
|
||||
b = GETJSAMPLE(inptr[RGB_BLUE]);
|
||||
inptr += RGB_PIXELSIZE;
|
||||
/* Y */
|
||||
outptr[col] = (JSAMPLE)
|
||||
((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF])
|
||||
>> SCALEBITS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert some rows of samples to the JPEG colorspace.
|
||||
* This version handles Adobe-style CMYK->YCCK conversion,
|
||||
* where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same
|
||||
* conversion as above, while passing K (black) unchanged.
|
||||
* We assume rgb_ycc_start has been called.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
cmyk_ycck_convert (j_compress_ptr cinfo,
|
||||
JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
|
||||
JDIMENSION output_row, int num_rows)
|
||||
{
|
||||
my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
|
||||
register int r, g, b;
|
||||
register INT32 * ctab = cconvert->rgb_ycc_tab;
|
||||
register JSAMPROW inptr;
|
||||
register JSAMPROW outptr0, outptr1, outptr2, outptr3;
|
||||
register JDIMENSION col;
|
||||
JDIMENSION num_cols = cinfo->image_width;
|
||||
|
||||
while (--num_rows >= 0) {
|
||||
inptr = *input_buf++;
|
||||
outptr0 = output_buf[0][output_row];
|
||||
outptr1 = output_buf[1][output_row];
|
||||
outptr2 = output_buf[2][output_row];
|
||||
outptr3 = output_buf[3][output_row];
|
||||
output_row++;
|
||||
for (col = 0; col < num_cols; col++) {
|
||||
r = MAXJSAMPLE - GETJSAMPLE(inptr[0]);
|
||||
g = MAXJSAMPLE - GETJSAMPLE(inptr[1]);
|
||||
b = MAXJSAMPLE - GETJSAMPLE(inptr[2]);
|
||||
/* K passes through as-is */
|
||||
outptr3[col] = inptr[3]; /* don't need GETJSAMPLE here */
|
||||
inptr += 4;
|
||||
/* If the inputs are 0..MAXJSAMPLE, the outputs of these equations
|
||||
* must be too; we do not need an explicit range-limiting operation.
|
||||
* Hence the value being shifted is never negative, and we don't
|
||||
* need the general RIGHT_SHIFT macro.
|
||||
*/
|
||||
/* Y */
|
||||
outptr0[col] = (JSAMPLE)
|
||||
((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF])
|
||||
>> SCALEBITS);
|
||||
/* Cb */
|
||||
outptr1[col] = (JSAMPLE)
|
||||
((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF])
|
||||
>> SCALEBITS);
|
||||
/* Cr */
|
||||
outptr2[col] = (JSAMPLE)
|
||||
((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF])
|
||||
>> SCALEBITS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert some rows of samples to the JPEG colorspace.
|
||||
* This version handles grayscale output with no conversion.
|
||||
* The source can be either plain grayscale or YCbCr (since Y == gray).
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
grayscale_convert (j_compress_ptr cinfo,
|
||||
JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
|
||||
JDIMENSION output_row, int num_rows)
|
||||
{
|
||||
register JSAMPROW inptr;
|
||||
register JSAMPROW outptr;
|
||||
register JDIMENSION col;
|
||||
JDIMENSION num_cols = cinfo->image_width;
|
||||
int instride = cinfo->input_components;
|
||||
|
||||
while (--num_rows >= 0) {
|
||||
inptr = *input_buf++;
|
||||
outptr = output_buf[0][output_row];
|
||||
output_row++;
|
||||
for (col = 0; col < num_cols; col++) {
|
||||
outptr[col] = inptr[0]; /* don't need GETJSAMPLE() here */
|
||||
inptr += instride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert some rows of samples to the JPEG colorspace.
|
||||
* This version handles multi-component colorspaces without conversion.
|
||||
* We assume input_components == num_components.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
null_convert (j_compress_ptr cinfo,
|
||||
JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
|
||||
JDIMENSION output_row, int num_rows)
|
||||
{
|
||||
register JSAMPROW inptr;
|
||||
register JSAMPROW outptr;
|
||||
register JDIMENSION col;
|
||||
register int ci;
|
||||
int nc = cinfo->num_components;
|
||||
JDIMENSION num_cols = cinfo->image_width;
|
||||
|
||||
while (--num_rows >= 0) {
|
||||
/* It seems fastest to make a separate pass for each component. */
|
||||
for (ci = 0; ci < nc; ci++) {
|
||||
inptr = *input_buf;
|
||||
outptr = output_buf[ci][output_row];
|
||||
for (col = 0; col < num_cols; col++) {
|
||||
outptr[col] = inptr[ci]; /* don't need GETJSAMPLE() here */
|
||||
inptr += nc;
|
||||
}
|
||||
}
|
||||
input_buf++;
|
||||
output_row++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Empty method for start_pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
null_method (j_compress_ptr cinfo)
|
||||
{
|
||||
/* no work needed */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Module initialization routine for input colorspace conversion.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_color_converter (j_compress_ptr cinfo)
|
||||
{
|
||||
my_cconvert_ptr cconvert;
|
||||
|
||||
cconvert = (my_cconvert_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_color_converter));
|
||||
cinfo->cconvert = (struct jpeg_color_converter *) cconvert;
|
||||
/* set start_pass to null method until we find out differently */
|
||||
cconvert->pub.start_pass = null_method;
|
||||
|
||||
/* Make sure input_components agrees with in_color_space */
|
||||
switch (cinfo->in_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
if (cinfo->input_components != 1)
|
||||
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
|
||||
break;
|
||||
|
||||
case JCS_RGB:
|
||||
#if RGB_PIXELSIZE != 3
|
||||
if (cinfo->input_components != RGB_PIXELSIZE)
|
||||
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
|
||||
break;
|
||||
#endif /* else share code with YCbCr */
|
||||
|
||||
case JCS_YCbCr:
|
||||
if (cinfo->input_components != 3)
|
||||
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
|
||||
break;
|
||||
|
||||
case JCS_CMYK:
|
||||
case JCS_YCCK:
|
||||
if (cinfo->input_components != 4)
|
||||
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
|
||||
break;
|
||||
|
||||
default: /* JCS_UNKNOWN can be anything */
|
||||
if (cinfo->input_components < 1)
|
||||
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check num_components, set conversion method based on requested space */
|
||||
switch (cinfo->jpeg_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
if (cinfo->num_components != 1)
|
||||
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
|
||||
if (cinfo->in_color_space == JCS_GRAYSCALE)
|
||||
cconvert->pub.color_convert = grayscale_convert;
|
||||
else if (cinfo->in_color_space == JCS_RGB) {
|
||||
cconvert->pub.start_pass = rgb_ycc_start;
|
||||
cconvert->pub.color_convert = rgb_gray_convert;
|
||||
} else if (cinfo->in_color_space == JCS_YCbCr)
|
||||
cconvert->pub.color_convert = grayscale_convert;
|
||||
else
|
||||
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
|
||||
break;
|
||||
|
||||
case JCS_RGB:
|
||||
if (cinfo->num_components != 3)
|
||||
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
|
||||
if (cinfo->in_color_space == JCS_RGB && RGB_PIXELSIZE == 3)
|
||||
cconvert->pub.color_convert = null_convert;
|
||||
else
|
||||
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
|
||||
break;
|
||||
|
||||
case JCS_YCbCr:
|
||||
if (cinfo->num_components != 3)
|
||||
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
|
||||
if (cinfo->in_color_space == JCS_RGB) {
|
||||
cconvert->pub.start_pass = rgb_ycc_start;
|
||||
cconvert->pub.color_convert = rgb_ycc_convert;
|
||||
} else if (cinfo->in_color_space == JCS_YCbCr)
|
||||
cconvert->pub.color_convert = null_convert;
|
||||
else
|
||||
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
|
||||
break;
|
||||
|
||||
case JCS_CMYK:
|
||||
if (cinfo->num_components != 4)
|
||||
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
|
||||
if (cinfo->in_color_space == JCS_CMYK)
|
||||
cconvert->pub.color_convert = null_convert;
|
||||
else
|
||||
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
|
||||
break;
|
||||
|
||||
case JCS_YCCK:
|
||||
if (cinfo->num_components != 4)
|
||||
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
|
||||
if (cinfo->in_color_space == JCS_CMYK) {
|
||||
cconvert->pub.start_pass = rgb_ycc_start;
|
||||
cconvert->pub.color_convert = cmyk_ycck_convert;
|
||||
} else if (cinfo->in_color_space == JCS_YCCK)
|
||||
cconvert->pub.color_convert = null_convert;
|
||||
else
|
||||
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
|
||||
break;
|
||||
|
||||
default: /* allow null conversion of JCS_UNKNOWN */
|
||||
if (cinfo->jpeg_color_space != cinfo->in_color_space ||
|
||||
cinfo->num_components != cinfo->input_components)
|
||||
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
|
||||
cconvert->pub.color_convert = null_convert;
|
||||
break;
|
||||
}
|
||||
}
|
||||
388
neo/renderer/jpeg-6/jcdctmgr.c
Normal file
388
neo/renderer/jpeg-6/jcdctmgr.c
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* jcdctmgr.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains the forward-DCT management logic.
|
||||
* This code selects a particular DCT implementation to be used,
|
||||
* and it performs related housekeeping chores including coefficient
|
||||
* quantization.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jdct.h" /* Private declarations for DCT subsystem */
|
||||
|
||||
|
||||
/* Private subobject for this module */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_forward_dct pub; /* public fields */
|
||||
|
||||
/* Pointer to the DCT routine actually in use */
|
||||
forward_DCT_method_ptr do_dct;
|
||||
|
||||
/* The actual post-DCT divisors --- not identical to the quant table
|
||||
* entries, because of scaling (especially for an unnormalized DCT).
|
||||
* Each table is given in normal array order; note that this must
|
||||
* be converted from the zigzag order of the quantization tables.
|
||||
*/
|
||||
DCTELEM * divisors[NUM_QUANT_TBLS];
|
||||
|
||||
#ifdef DCT_FLOAT_SUPPORTED
|
||||
/* Same as above for the floating-point case. */
|
||||
float_DCT_method_ptr do_float_dct;
|
||||
FAST_FLOAT * float_divisors[NUM_QUANT_TBLS];
|
||||
#endif
|
||||
} my_fdct_controller;
|
||||
|
||||
typedef my_fdct_controller * my_fdct_ptr;
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a processing pass.
|
||||
* Verify that all referenced Q-tables are present, and set up
|
||||
* the divisor table for each one.
|
||||
* In the current implementation, DCT of all components is done during
|
||||
* the first pass, even if only some components will be output in the
|
||||
* first scan. Hence all components should be examined here.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_fdctmgr (j_compress_ptr cinfo)
|
||||
{
|
||||
my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct;
|
||||
int ci, qtblno, i;
|
||||
jpeg_component_info *compptr;
|
||||
JQUANT_TBL * qtbl;
|
||||
//DCTELEM * dtbl;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
qtblno = compptr->quant_tbl_no;
|
||||
/* Make sure specified quantization table is present */
|
||||
if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS ||
|
||||
cinfo->quant_tbl_ptrs[qtblno] == NULL)
|
||||
ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno);
|
||||
qtbl = cinfo->quant_tbl_ptrs[qtblno];
|
||||
/* Compute divisors for this quant table */
|
||||
/* We may do this more than once for same table, but it's not a big deal */
|
||||
switch (cinfo->dct_method) {
|
||||
#ifdef DCT_ISLOW_SUPPORTED
|
||||
case JDCT_ISLOW:
|
||||
/* For LL&M IDCT method, divisors are equal to raw quantization
|
||||
* coefficients multiplied by 8 (to counteract scaling).
|
||||
*/
|
||||
if (fdct->divisors[qtblno] == NULL) {
|
||||
fdct->divisors[qtblno] = (DCTELEM *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
DCTSIZE2 * SIZEOF(DCTELEM));
|
||||
}
|
||||
dtbl = fdct->divisors[qtblno];
|
||||
for (i = 0; i < DCTSIZE2; i++) {
|
||||
dtbl[i] = ((DCTELEM) qtbl->quantval[jpeg_zigzag_order[i]]) << 3;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef DCT_IFAST_SUPPORTED
|
||||
case JDCT_IFAST:
|
||||
{
|
||||
/* For AA&N IDCT method, divisors are equal to quantization
|
||||
* coefficients scaled by scalefactor[row]*scalefactor[col], where
|
||||
* scalefactor[0] = 1
|
||||
* scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7
|
||||
* We apply a further scale factor of 8.
|
||||
*/
|
||||
#define CONST_BITS 14
|
||||
static const INT16 aanscales[DCTSIZE2] = {
|
||||
/* precomputed values scaled up by 14 bits: in natural order */
|
||||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520,
|
||||
22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270,
|
||||
21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906,
|
||||
19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315,
|
||||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520,
|
||||
12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552,
|
||||
8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446,
|
||||
4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247
|
||||
};
|
||||
SHIFT_TEMPS
|
||||
|
||||
if (fdct->divisors[qtblno] == NULL) {
|
||||
fdct->divisors[qtblno] = (DCTELEM *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
DCTSIZE2 * SIZEOF(DCTELEM));
|
||||
}
|
||||
dtbl = fdct->divisors[qtblno];
|
||||
for (i = 0; i < DCTSIZE2; i++) {
|
||||
dtbl[i] = (DCTELEM)
|
||||
DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[jpeg_zigzag_order[i]],
|
||||
(INT32) aanscales[i]),
|
||||
CONST_BITS-3);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef DCT_FLOAT_SUPPORTED
|
||||
case JDCT_FLOAT:
|
||||
{
|
||||
/* For float AA&N IDCT method, divisors are equal to quantization
|
||||
* coefficients scaled by scalefactor[row]*scalefactor[col], where
|
||||
* scalefactor[0] = 1
|
||||
* scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7
|
||||
* We apply a further scale factor of 8.
|
||||
* What's actually stored is 1/divisor so that the inner loop can
|
||||
* use a multiplication rather than a division.
|
||||
*/
|
||||
FAST_FLOAT * fdtbl;
|
||||
int row, col;
|
||||
static const double aanscalefactor[DCTSIZE] = {
|
||||
1.0, 1.387039845, 1.306562965, 1.175875602,
|
||||
1.0, 0.785694958, 0.541196100, 0.275899379
|
||||
};
|
||||
|
||||
if (fdct->float_divisors[qtblno] == NULL) {
|
||||
fdct->float_divisors[qtblno] = (FAST_FLOAT *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
DCTSIZE2 * SIZEOF(FAST_FLOAT));
|
||||
}
|
||||
fdtbl = fdct->float_divisors[qtblno];
|
||||
i = 0;
|
||||
for (row = 0; row < DCTSIZE; row++) {
|
||||
for (col = 0; col < DCTSIZE; col++) {
|
||||
fdtbl[i] = (FAST_FLOAT)
|
||||
(1.0 / (((double) qtbl->quantval[jpeg_zigzag_order[i]] *
|
||||
aanscalefactor[row] * aanscalefactor[col] * 8.0)));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Perform forward DCT on one or more blocks of a component.
|
||||
*
|
||||
* The input samples are taken from the sample_data[] array starting at
|
||||
* position start_row/start_col, and moving to the right for any additional
|
||||
* blocks. The quantized coefficients are returned in coef_blocks[].
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY sample_data, JBLOCKROW coef_blocks,
|
||||
JDIMENSION start_row, JDIMENSION start_col,
|
||||
JDIMENSION num_blocks)
|
||||
/* This version is used for integer DCT implementations. */
|
||||
{
|
||||
/* This routine is heavily used, so it's worth coding it tightly. */
|
||||
my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct;
|
||||
forward_DCT_method_ptr do_dct = fdct->do_dct;
|
||||
DCTELEM * divisors = fdct->divisors[compptr->quant_tbl_no];
|
||||
DCTELEM workspace[DCTSIZE2]; /* work area for FDCT subroutine */
|
||||
JDIMENSION bi;
|
||||
|
||||
sample_data += start_row; /* fold in the vertical offset once */
|
||||
|
||||
for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) {
|
||||
/* Load data into workspace, applying unsigned->signed conversion */
|
||||
{ register DCTELEM *workspaceptr;
|
||||
register JSAMPROW elemptr;
|
||||
register int elemr;
|
||||
|
||||
workspaceptr = workspace;
|
||||
for (elemr = 0; elemr < DCTSIZE; elemr++) {
|
||||
elemptr = sample_data[elemr] + start_col;
|
||||
#if DCTSIZE == 8 /* unroll the inner loop */
|
||||
*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
|
||||
*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
|
||||
*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
|
||||
*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
|
||||
*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
|
||||
*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
|
||||
*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
|
||||
*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
|
||||
#else
|
||||
{ register int elemc;
|
||||
for (elemc = DCTSIZE; elemc > 0; elemc--) {
|
||||
*workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform the DCT */
|
||||
(*do_dct) (workspace);
|
||||
|
||||
/* Quantize/descale the coefficients, and store into coef_blocks[] */
|
||||
{ register DCTELEM temp, qval;
|
||||
register int i;
|
||||
register JCOEFPTR output_ptr = coef_blocks[bi];
|
||||
|
||||
for (i = 0; i < DCTSIZE2; i++) {
|
||||
qval = divisors[i];
|
||||
temp = workspace[i];
|
||||
/* Divide the coefficient value by qval, ensuring proper rounding.
|
||||
* Since C does not specify the direction of rounding for negative
|
||||
* quotients, we have to force the dividend positive for portability.
|
||||
*
|
||||
* In most files, at least half of the output values will be zero
|
||||
* (at default quantization settings, more like three-quarters...)
|
||||
* so we should ensure that this case is fast. On many machines,
|
||||
* a comparison is enough cheaper than a divide to make a special test
|
||||
* a win. Since both inputs will be nonnegative, we need only test
|
||||
* for a < b to discover whether a/b is 0.
|
||||
* If your machine's division is fast enough, define FAST_DIVIDE.
|
||||
*/
|
||||
#ifdef FAST_DIVIDE
|
||||
#define DIVIDE_BY(a,b) a /= b
|
||||
#else
|
||||
#define DIVIDE_BY(a,b) if (a >= b) a /= b; else a = 0
|
||||
#endif
|
||||
if (temp < 0) {
|
||||
temp = -temp;
|
||||
temp += qval>>1; /* for rounding */
|
||||
DIVIDE_BY(temp, qval);
|
||||
temp = -temp;
|
||||
} else {
|
||||
temp += qval>>1; /* for rounding */
|
||||
DIVIDE_BY(temp, qval);
|
||||
}
|
||||
output_ptr[i] = (JCOEF) temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DCT_FLOAT_SUPPORTED
|
||||
|
||||
METHODDEF void
|
||||
forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY sample_data, JBLOCKROW coef_blocks,
|
||||
JDIMENSION start_row, JDIMENSION start_col,
|
||||
JDIMENSION num_blocks)
|
||||
/* This version is used for floating-point DCT implementations. */
|
||||
{
|
||||
/* This routine is heavily used, so it's worth coding it tightly. */
|
||||
my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct;
|
||||
float_DCT_method_ptr do_dct = fdct->do_float_dct;
|
||||
FAST_FLOAT * divisors = fdct->float_divisors[compptr->quant_tbl_no];
|
||||
FAST_FLOAT workspace[DCTSIZE2]; /* work area for FDCT subroutine */
|
||||
JDIMENSION bi;
|
||||
|
||||
sample_data += start_row; /* fold in the vertical offset once */
|
||||
|
||||
for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) {
|
||||
/* Load data into workspace, applying unsigned->signed conversion */
|
||||
{ register FAST_FLOAT *workspaceptr;
|
||||
register JSAMPROW elemptr;
|
||||
register int elemr;
|
||||
|
||||
workspaceptr = workspace;
|
||||
for (elemr = 0; elemr < DCTSIZE; elemr++) {
|
||||
elemptr = sample_data[elemr] + start_col;
|
||||
#if DCTSIZE == 8 /* unroll the inner loop */
|
||||
*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
|
||||
*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
|
||||
*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
|
||||
*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
|
||||
*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
|
||||
*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
|
||||
*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
|
||||
*workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
|
||||
#else
|
||||
{ register int elemc;
|
||||
for (elemc = DCTSIZE; elemc > 0; elemc--) {
|
||||
*workspaceptr++ = (FAST_FLOAT)
|
||||
(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform the DCT */
|
||||
(*do_dct) (workspace);
|
||||
|
||||
/* Quantize/descale the coefficients, and store into coef_blocks[] */
|
||||
{ register FAST_FLOAT temp;
|
||||
register int i;
|
||||
register JCOEFPTR output_ptr = coef_blocks[bi];
|
||||
|
||||
for (i = 0; i < DCTSIZE2; i++) {
|
||||
/* Apply the quantization and scaling factor */
|
||||
temp = workspace[i] * divisors[i];
|
||||
/* Round to nearest integer.
|
||||
* Since C does not specify the direction of rounding for negative
|
||||
* quotients, we have to force the dividend positive for portability.
|
||||
* The maximum coefficient size is +-16K (for 12-bit data), so this
|
||||
* code should work for either 16-bit or 32-bit ints.
|
||||
*/
|
||||
output_ptr[i] = (JCOEF) ((int) (temp + (FAST_FLOAT) 16384.5) - 16384);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DCT_FLOAT_SUPPORTED */
|
||||
|
||||
|
||||
/*
|
||||
* Initialize FDCT manager.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_forward_dct (j_compress_ptr cinfo)
|
||||
{
|
||||
my_fdct_ptr fdct;
|
||||
int i;
|
||||
|
||||
fdct = (my_fdct_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_fdct_controller));
|
||||
cinfo->fdct = (struct jpeg_forward_dct *) fdct;
|
||||
fdct->pub.start_pass = start_pass_fdctmgr;
|
||||
|
||||
switch (cinfo->dct_method) {
|
||||
#ifdef DCT_ISLOW_SUPPORTED
|
||||
case JDCT_ISLOW:
|
||||
fdct->pub.forward_DCT = forward_DCT;
|
||||
fdct->do_dct = jpeg_fdct_islow;
|
||||
break;
|
||||
#endif
|
||||
#ifdef DCT_IFAST_SUPPORTED
|
||||
case JDCT_IFAST:
|
||||
fdct->pub.forward_DCT = forward_DCT;
|
||||
fdct->do_dct = jpeg_fdct_ifast;
|
||||
break;
|
||||
#endif
|
||||
#ifdef DCT_FLOAT_SUPPORTED
|
||||
case JDCT_FLOAT:
|
||||
fdct->pub.forward_DCT = forward_DCT_float;
|
||||
fdct->do_float_dct = jpeg_fdct_float;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Mark divisor tables unallocated */
|
||||
for (i = 0; i < NUM_QUANT_TBLS; i++) {
|
||||
fdct->divisors[i] = NULL;
|
||||
#ifdef DCT_FLOAT_SUPPORTED
|
||||
fdct->float_divisors[i] = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
846
neo/renderer/jpeg-6/jchuff.c
Normal file
846
neo/renderer/jpeg-6/jchuff.c
Normal file
@@ -0,0 +1,846 @@
|
||||
/*
|
||||
* jchuff.c
|
||||
*
|
||||
* Copyright (C) 1991-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains Huffman entropy encoding routines.
|
||||
*
|
||||
* Much of the complexity here has to do with supporting output suspension.
|
||||
* If the data destination module demands suspension, we want to be able to
|
||||
* back up to the start of the current MCU. To do this, we copy state
|
||||
* variables into local working storage, and update them back to the
|
||||
* permanent JPEG objects only upon successful completion of an MCU.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jchuff.h" /* Declarations shared with jcphuff.c */
|
||||
|
||||
|
||||
/* Expanded entropy encoder object for Huffman encoding.
|
||||
*
|
||||
* The savable_state subrecord contains fields that change within an MCU,
|
||||
* but must not be updated permanently until we complete the MCU.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
INT32 put_buffer; /* current bit-accumulation buffer */
|
||||
int put_bits; /* # of bits now in it */
|
||||
int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */
|
||||
} savable_state;
|
||||
|
||||
/* This macro is to work around compilers with missing or broken
|
||||
* structure assignment. You'll need to fix this code if you have
|
||||
* such a compiler and you change MAX_COMPS_IN_SCAN.
|
||||
*/
|
||||
|
||||
#ifndef NO_STRUCT_ASSIGN
|
||||
#define ASSIGN_STATE(dest,src) ((dest) = (src))
|
||||
#else
|
||||
#if MAX_COMPS_IN_SCAN == 4
|
||||
#define ASSIGN_STATE(dest,src) \
|
||||
((dest).put_buffer = (src).put_buffer, \
|
||||
(dest).put_bits = (src).put_bits, \
|
||||
(dest).last_dc_val[0] = (src).last_dc_val[0], \
|
||||
(dest).last_dc_val[1] = (src).last_dc_val[1], \
|
||||
(dest).last_dc_val[2] = (src).last_dc_val[2], \
|
||||
(dest).last_dc_val[3] = (src).last_dc_val[3])
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_entropy_encoder pub; /* public fields */
|
||||
|
||||
savable_state saved; /* Bit buffer & DC state at start of MCU */
|
||||
|
||||
/* These fields are NOT loaded into local working state. */
|
||||
unsigned int restarts_to_go; /* MCUs left in this restart interval */
|
||||
int next_restart_num; /* next restart number to write (0-7) */
|
||||
|
||||
/* Pointers to derived tables (these workspaces have image lifespan) */
|
||||
c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS];
|
||||
c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS];
|
||||
|
||||
#ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */
|
||||
long * dc_count_ptrs[NUM_HUFF_TBLS];
|
||||
long * ac_count_ptrs[NUM_HUFF_TBLS];
|
||||
#endif
|
||||
} huff_entropy_encoder;
|
||||
|
||||
typedef huff_entropy_encoder * huff_entropy_ptr;
|
||||
|
||||
/* Working state while writing an MCU.
|
||||
* This struct contains all the fields that are needed by subroutines.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
JOCTET * next_output_byte; /* => next byte to write in buffer */
|
||||
size_t free_in_buffer; /* # of byte spaces remaining in buffer */
|
||||
savable_state cur; /* Current bit buffer & DC state */
|
||||
j_compress_ptr cinfo; /* dump_buffer needs access to this */
|
||||
} working_state;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
METHODDEF boolean encode_mcu_huff JPP((j_compress_ptr cinfo,
|
||||
JBLOCKROW *MCU_data));
|
||||
METHODDEF void finish_pass_huff JPP((j_compress_ptr cinfo));
|
||||
#ifdef ENTROPY_OPT_SUPPORTED
|
||||
METHODDEF boolean encode_mcu_gather JPP((j_compress_ptr cinfo,
|
||||
JBLOCKROW *MCU_data));
|
||||
METHODDEF void finish_pass_gather JPP((j_compress_ptr cinfo));
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a Huffman-compressed scan.
|
||||
* If gather_statistics is TRUE, we do not output anything during the scan,
|
||||
* just count the Huffman symbols used and generate Huffman code tables.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics)
|
||||
{
|
||||
huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
|
||||
int ci, dctbl, actbl;
|
||||
jpeg_component_info * compptr;
|
||||
|
||||
if (gather_statistics) {
|
||||
#ifdef ENTROPY_OPT_SUPPORTED
|
||||
entropy->pub.encode_mcu = encode_mcu_gather;
|
||||
entropy->pub.finish_pass = finish_pass_gather;
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
} else {
|
||||
entropy->pub.encode_mcu = encode_mcu_huff;
|
||||
entropy->pub.finish_pass = finish_pass_huff;
|
||||
}
|
||||
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
dctbl = compptr->dc_tbl_no;
|
||||
actbl = compptr->ac_tbl_no;
|
||||
/* Make sure requested tables are present */
|
||||
/* (In gather mode, tables need not be allocated yet) */
|
||||
if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS ||
|
||||
(cinfo->dc_huff_tbl_ptrs[dctbl] == NULL && !gather_statistics))
|
||||
ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl);
|
||||
if (actbl < 0 || actbl >= NUM_HUFF_TBLS ||
|
||||
(cinfo->ac_huff_tbl_ptrs[actbl] == NULL && !gather_statistics))
|
||||
ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl);
|
||||
if (gather_statistics) {
|
||||
#ifdef ENTROPY_OPT_SUPPORTED
|
||||
/* Allocate and zero the statistics tables */
|
||||
/* Note that jpeg_gen_optimal_table expects 257 entries in each table! */
|
||||
if (entropy->dc_count_ptrs[dctbl] == NULL)
|
||||
entropy->dc_count_ptrs[dctbl] = (long *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
257 * SIZEOF(long));
|
||||
MEMZERO(entropy->dc_count_ptrs[dctbl], 257 * SIZEOF(long));
|
||||
if (entropy->ac_count_ptrs[actbl] == NULL)
|
||||
entropy->ac_count_ptrs[actbl] = (long *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
257 * SIZEOF(long));
|
||||
MEMZERO(entropy->ac_count_ptrs[actbl], 257 * SIZEOF(long));
|
||||
#endif
|
||||
} else {
|
||||
/* Compute derived values for Huffman tables */
|
||||
/* We may do this more than once for a table, but it's not expensive */
|
||||
jpeg_make_c_derived_tbl(cinfo, cinfo->dc_huff_tbl_ptrs[dctbl],
|
||||
& entropy->dc_derived_tbls[dctbl]);
|
||||
jpeg_make_c_derived_tbl(cinfo, cinfo->ac_huff_tbl_ptrs[actbl],
|
||||
& entropy->ac_derived_tbls[actbl]);
|
||||
}
|
||||
/* Initialize DC predictions to 0 */
|
||||
entropy->saved.last_dc_val[ci] = 0;
|
||||
}
|
||||
|
||||
/* Initialize bit buffer to empty */
|
||||
entropy->saved.put_buffer = 0;
|
||||
entropy->saved.put_bits = 0;
|
||||
|
||||
/* Initialize restart stuff */
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
entropy->next_restart_num = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compute the derived values for a Huffman table.
|
||||
* Note this is also used by jcphuff.c.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_make_c_derived_tbl (j_compress_ptr cinfo, JHUFF_TBL * htbl,
|
||||
c_derived_tbl ** pdtbl)
|
||||
{
|
||||
c_derived_tbl *dtbl;
|
||||
int p, i, l, lastp, si;
|
||||
char huffsize[257];
|
||||
unsigned int huffcode[257];
|
||||
unsigned int code;
|
||||
|
||||
/* Allocate a workspace if we haven't already done so. */
|
||||
if (*pdtbl == NULL)
|
||||
*pdtbl = (c_derived_tbl *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(c_derived_tbl));
|
||||
dtbl = *pdtbl;
|
||||
|
||||
/* Figure C.1: make table of Huffman code length for each symbol */
|
||||
/* Note that this is in code-length order. */
|
||||
|
||||
p = 0;
|
||||
for (l = 1; l <= 16; l++) {
|
||||
for (i = 1; i <= (int) htbl->bits[l]; i++)
|
||||
huffsize[p++] = (char) l;
|
||||
}
|
||||
huffsize[p] = 0;
|
||||
lastp = p;
|
||||
|
||||
/* Figure C.2: generate the codes themselves */
|
||||
/* Note that this is in code-length order. */
|
||||
|
||||
code = 0;
|
||||
si = huffsize[0];
|
||||
p = 0;
|
||||
while (huffsize[p]) {
|
||||
while (((int) huffsize[p]) == si) {
|
||||
huffcode[p++] = code;
|
||||
code++;
|
||||
}
|
||||
code <<= 1;
|
||||
si++;
|
||||
}
|
||||
|
||||
/* Figure C.3: generate encoding tables */
|
||||
/* These are code and size indexed by symbol value */
|
||||
|
||||
/* Set any codeless symbols to have code length 0;
|
||||
* this allows emit_bits to detect any attempt to emit such symbols.
|
||||
*/
|
||||
MEMZERO(dtbl->ehufsi, SIZEOF(dtbl->ehufsi));
|
||||
|
||||
for (p = 0; p < lastp; p++) {
|
||||
dtbl->ehufco[htbl->huffval[p]] = huffcode[p];
|
||||
dtbl->ehufsi[htbl->huffval[p]] = huffsize[p];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Outputting bytes to the file */
|
||||
|
||||
/* Emit a byte, taking 'action' if must suspend. */
|
||||
#define emit_byte(state,val,action) \
|
||||
{ *(state)->next_output_byte++ = (JOCTET) (val); \
|
||||
if (--(state)->free_in_buffer == 0) \
|
||||
if (! dump_buffer(state)) \
|
||||
{ action; } }
|
||||
|
||||
|
||||
LOCAL boolean
|
||||
dump_buffer (working_state * state)
|
||||
/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */
|
||||
{
|
||||
struct jpeg_destination_mgr * dest = state->cinfo->dest;
|
||||
|
||||
if (! (*dest->empty_output_buffer) (state->cinfo))
|
||||
return FALSE;
|
||||
/* After a successful buffer dump, must reset buffer pointers */
|
||||
state->next_output_byte = dest->next_output_byte;
|
||||
state->free_in_buffer = dest->free_in_buffer;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Outputting bits to the file */
|
||||
|
||||
/* Only the right 24 bits of put_buffer are used; the valid bits are
|
||||
* left-justified in this part. At most 16 bits can be passed to emit_bits
|
||||
* in one call, and we never retain more than 7 bits in put_buffer
|
||||
* between calls, so 24 bits are sufficient.
|
||||
*/
|
||||
|
||||
INLINE
|
||||
LOCAL boolean
|
||||
emit_bits (working_state * state, unsigned int code, int size)
|
||||
/* Emit some bits; return TRUE if successful, FALSE if must suspend */
|
||||
{
|
||||
/* This routine is heavily used, so it's worth coding tightly. */
|
||||
register INT32 put_buffer = (INT32) code;
|
||||
register int put_bits = state->cur.put_bits;
|
||||
|
||||
/* if size is 0, caller used an invalid Huffman table entry */
|
||||
if (size == 0)
|
||||
ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE);
|
||||
|
||||
put_buffer &= (((INT32) 1)<<size) - 1; /* mask off any extra bits in code */
|
||||
|
||||
put_bits += size; /* new number of bits in buffer */
|
||||
|
||||
put_buffer <<= 24 - put_bits; /* align incoming bits */
|
||||
|
||||
put_buffer |= state->cur.put_buffer; /* and merge with old buffer contents */
|
||||
|
||||
while (put_bits >= 8) {
|
||||
int c = (int) ((put_buffer >> 16) & 0xFF);
|
||||
|
||||
emit_byte(state, c, return FALSE);
|
||||
if (c == 0xFF) { /* need to stuff a zero byte? */
|
||||
emit_byte(state, 0, return FALSE);
|
||||
}
|
||||
put_buffer <<= 8;
|
||||
put_bits -= 8;
|
||||
}
|
||||
|
||||
state->cur.put_buffer = put_buffer; /* update state variables */
|
||||
state->cur.put_bits = put_bits;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
LOCAL boolean
|
||||
flush_bits (working_state * state)
|
||||
{
|
||||
if (! emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */
|
||||
return FALSE;
|
||||
state->cur.put_buffer = 0; /* and reset bit-buffer to empty */
|
||||
state->cur.put_bits = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Encode a single block's worth of coefficients */
|
||||
|
||||
LOCAL boolean
|
||||
encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val,
|
||||
c_derived_tbl *dctbl, c_derived_tbl *actbl)
|
||||
{
|
||||
register int temp, temp2;
|
||||
register int nbits;
|
||||
register int k, r, i;
|
||||
|
||||
/* Encode the DC coefficient difference per section F.1.2.1 */
|
||||
|
||||
temp = temp2 = block[0] - last_dc_val;
|
||||
|
||||
if (temp < 0) {
|
||||
temp = -temp; /* temp is abs value of input */
|
||||
/* For a negative input, want temp2 = bitwise complement of abs(input) */
|
||||
/* This code assumes we are on a two's complement machine */
|
||||
temp2--;
|
||||
}
|
||||
|
||||
/* Find the number of bits needed for the magnitude of the coefficient */
|
||||
nbits = 0;
|
||||
while (temp) {
|
||||
nbits++;
|
||||
temp >>= 1;
|
||||
}
|
||||
|
||||
/* Emit the Huffman-coded symbol for the number of bits */
|
||||
if (! emit_bits(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits]))
|
||||
return FALSE;
|
||||
|
||||
/* Emit that number of bits of the value, if positive, */
|
||||
/* or the complement of its magnitude, if negative. */
|
||||
if (nbits) /* emit_bits rejects calls with size 0 */
|
||||
if (! emit_bits(state, (unsigned int) temp2, nbits))
|
||||
return FALSE;
|
||||
|
||||
/* Encode the AC coefficients per section F.1.2.2 */
|
||||
|
||||
r = 0; /* r = run length of zeros */
|
||||
|
||||
for (k = 1; k < DCTSIZE2; k++) {
|
||||
if ((temp = block[jpeg_natural_order[k]]) == 0) {
|
||||
r++;
|
||||
} else {
|
||||
/* if run length > 15, must emit special run-length-16 codes (0xF0) */
|
||||
while (r > 15) {
|
||||
if (! emit_bits(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0]))
|
||||
return FALSE;
|
||||
r -= 16;
|
||||
}
|
||||
|
||||
temp2 = temp;
|
||||
if (temp < 0) {
|
||||
temp = -temp; /* temp is abs value of input */
|
||||
/* This code assumes we are on a two's complement machine */
|
||||
temp2--;
|
||||
}
|
||||
|
||||
/* Find the number of bits needed for the magnitude of the coefficient */
|
||||
nbits = 1; /* there must be at least one 1 bit */
|
||||
while ((temp >>= 1))
|
||||
nbits++;
|
||||
|
||||
/* Emit Huffman symbol for run length / number of bits */
|
||||
i = (r << 4) + nbits;
|
||||
if (! emit_bits(state, actbl->ehufco[i], actbl->ehufsi[i]))
|
||||
return FALSE;
|
||||
|
||||
/* Emit that number of bits of the value, if positive, */
|
||||
/* or the complement of its magnitude, if negative. */
|
||||
if (! emit_bits(state, (unsigned int) temp2, nbits))
|
||||
return FALSE;
|
||||
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the last coef(s) were zero, emit an end-of-block code */
|
||||
if (r > 0)
|
||||
if (! emit_bits(state, actbl->ehufco[0], actbl->ehufsi[0]))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Emit a restart marker & resynchronize predictions.
|
||||
*/
|
||||
|
||||
LOCAL boolean
|
||||
emit_restart (working_state * state, int restart_num)
|
||||
{
|
||||
int ci;
|
||||
|
||||
if (! flush_bits(state))
|
||||
return FALSE;
|
||||
|
||||
emit_byte(state, 0xFF, return FALSE);
|
||||
emit_byte(state, JPEG_RST0 + restart_num, return FALSE);
|
||||
|
||||
/* Re-initialize DC predictions to 0 */
|
||||
for (ci = 0; ci < state->cinfo->comps_in_scan; ci++)
|
||||
state->cur.last_dc_val[ci] = 0;
|
||||
|
||||
/* The restart counter is not updated until we successfully write the MCU. */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Encode and output one MCU's worth of Huffman-compressed coefficients.
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
{
|
||||
huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
|
||||
working_state state;
|
||||
int blkn, ci;
|
||||
jpeg_component_info * compptr;
|
||||
|
||||
/* Load up working state */
|
||||
state.next_output_byte = cinfo->dest->next_output_byte;
|
||||
state.free_in_buffer = cinfo->dest->free_in_buffer;
|
||||
ASSIGN_STATE(state.cur, entropy->saved);
|
||||
state.cinfo = cinfo;
|
||||
|
||||
/* Emit restart marker if needed */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0)
|
||||
if (! emit_restart(&state, entropy->next_restart_num))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Encode the MCU data blocks */
|
||||
for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
|
||||
ci = cinfo->MCU_membership[blkn];
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
if (! encode_one_block(&state,
|
||||
MCU_data[blkn][0], state.cur.last_dc_val[ci],
|
||||
entropy->dc_derived_tbls[compptr->dc_tbl_no],
|
||||
entropy->ac_derived_tbls[compptr->ac_tbl_no]))
|
||||
return FALSE;
|
||||
/* Update last_dc_val */
|
||||
state.cur.last_dc_val[ci] = MCU_data[blkn][0][0];
|
||||
}
|
||||
|
||||
/* Completed MCU, so update state */
|
||||
cinfo->dest->next_output_byte = state.next_output_byte;
|
||||
cinfo->dest->free_in_buffer = state.free_in_buffer;
|
||||
ASSIGN_STATE(entropy->saved, state.cur);
|
||||
|
||||
/* Update restart-interval state too */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0) {
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
entropy->next_restart_num++;
|
||||
entropy->next_restart_num &= 7;
|
||||
}
|
||||
entropy->restarts_to_go--;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish up at the end of a Huffman-compressed scan.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
finish_pass_huff (j_compress_ptr cinfo)
|
||||
{
|
||||
huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
|
||||
working_state state;
|
||||
|
||||
/* Load up working state ... flush_bits needs it */
|
||||
state.next_output_byte = cinfo->dest->next_output_byte;
|
||||
state.free_in_buffer = cinfo->dest->free_in_buffer;
|
||||
ASSIGN_STATE(state.cur, entropy->saved);
|
||||
state.cinfo = cinfo;
|
||||
|
||||
/* Flush out the last data */
|
||||
if (! flush_bits(&state))
|
||||
ERREXIT(cinfo, JERR_CANT_SUSPEND);
|
||||
|
||||
/* Update state */
|
||||
cinfo->dest->next_output_byte = state.next_output_byte;
|
||||
cinfo->dest->free_in_buffer = state.free_in_buffer;
|
||||
ASSIGN_STATE(entropy->saved, state.cur);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Huffman coding optimization.
|
||||
*
|
||||
* This actually is optimization, in the sense that we find the best possible
|
||||
* Huffman table(s) for the given data. We first scan the supplied data and
|
||||
* count the number of uses of each symbol that is to be Huffman-coded.
|
||||
* (This process must agree with the code above.) Then we build an
|
||||
* optimal Huffman coding tree for the observed counts.
|
||||
*
|
||||
* The JPEG standard requires Huffman codes to be no more than 16 bits long.
|
||||
* If some symbols have a very small but nonzero probability, the Huffman tree
|
||||
* must be adjusted to meet the code length restriction. We currently use
|
||||
* the adjustment method suggested in the JPEG spec. This method is *not*
|
||||
* optimal; it may not choose the best possible limited-length code. But
|
||||
* since the symbols involved are infrequently used, it's not clear that
|
||||
* going to extra trouble is worthwhile.
|
||||
*/
|
||||
|
||||
#ifdef ENTROPY_OPT_SUPPORTED
|
||||
|
||||
|
||||
/* Process a single block's worth of coefficients */
|
||||
|
||||
LOCAL void
|
||||
htest_one_block (JCOEFPTR block, int last_dc_val,
|
||||
long dc_counts[], long ac_counts[])
|
||||
{
|
||||
register int temp;
|
||||
register int nbits;
|
||||
register int k, r;
|
||||
|
||||
/* Encode the DC coefficient difference per section F.1.2.1 */
|
||||
|
||||
temp = block[0] - last_dc_val;
|
||||
if (temp < 0)
|
||||
temp = -temp;
|
||||
|
||||
/* Find the number of bits needed for the magnitude of the coefficient */
|
||||
nbits = 0;
|
||||
while (temp) {
|
||||
nbits++;
|
||||
temp >>= 1;
|
||||
}
|
||||
|
||||
/* Count the Huffman symbol for the number of bits */
|
||||
dc_counts[nbits]++;
|
||||
|
||||
/* Encode the AC coefficients per section F.1.2.2 */
|
||||
|
||||
r = 0; /* r = run length of zeros */
|
||||
|
||||
for (k = 1; k < DCTSIZE2; k++) {
|
||||
if ((temp = block[jpeg_natural_order[k]]) == 0) {
|
||||
r++;
|
||||
} else {
|
||||
/* if run length > 15, must emit special run-length-16 codes (0xF0) */
|
||||
while (r > 15) {
|
||||
ac_counts[0xF0]++;
|
||||
r -= 16;
|
||||
}
|
||||
|
||||
/* Find the number of bits needed for the magnitude of the coefficient */
|
||||
if (temp < 0)
|
||||
temp = -temp;
|
||||
|
||||
/* Find the number of bits needed for the magnitude of the coefficient */
|
||||
nbits = 1; /* there must be at least one 1 bit */
|
||||
while ((temp >>= 1))
|
||||
nbits++;
|
||||
|
||||
/* Count Huffman symbol for run length / number of bits */
|
||||
ac_counts[(r << 4) + nbits]++;
|
||||
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the last coef(s) were zero, emit an end-of-block code */
|
||||
if (r > 0)
|
||||
ac_counts[0]++;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Trial-encode one MCU's worth of Huffman-compressed coefficients.
|
||||
* No data is actually output, so no suspension return is possible.
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
{
|
||||
huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
|
||||
int blkn, ci;
|
||||
jpeg_component_info * compptr;
|
||||
|
||||
/* Take care of restart intervals if needed */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0) {
|
||||
/* Re-initialize DC predictions to 0 */
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++)
|
||||
entropy->saved.last_dc_val[ci] = 0;
|
||||
/* Update restart state */
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
}
|
||||
entropy->restarts_to_go--;
|
||||
}
|
||||
|
||||
for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
|
||||
ci = cinfo->MCU_membership[blkn];
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
htest_one_block(MCU_data[blkn][0], entropy->saved.last_dc_val[ci],
|
||||
entropy->dc_count_ptrs[compptr->dc_tbl_no],
|
||||
entropy->ac_count_ptrs[compptr->ac_tbl_no]);
|
||||
entropy->saved.last_dc_val[ci] = MCU_data[blkn][0][0];
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generate the optimal coding for the given counts, fill htbl.
|
||||
* Note this is also used by jcphuff.c.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[])
|
||||
{
|
||||
#define MAX_CLEN 32 /* assumed maximum initial code length */
|
||||
UINT8 bits[MAX_CLEN+1]; /* bits[k] = # of symbols with code length k */
|
||||
int codesize[257]; /* codesize[k] = code length of symbol k */
|
||||
int others[257]; /* next symbol in current branch of tree */
|
||||
int c1, c2;
|
||||
int p, i, j;
|
||||
long v;
|
||||
|
||||
/* This algorithm is explained in section K.2 of the JPEG standard */
|
||||
|
||||
MEMZERO(bits, SIZEOF(bits));
|
||||
MEMZERO(codesize, SIZEOF(codesize));
|
||||
for (i = 0; i < 257; i++)
|
||||
others[i] = -1; /* init links to empty */
|
||||
|
||||
freq[256] = 1; /* make sure there is a nonzero count */
|
||||
/* Including the pseudo-symbol 256 in the Huffman procedure guarantees
|
||||
* that no real symbol is given code-value of all ones, because 256
|
||||
* will be placed in the largest codeword category.
|
||||
*/
|
||||
|
||||
/* Huffman's basic algorithm to assign optimal code lengths to symbols */
|
||||
|
||||
for (;;) {
|
||||
/* Find the smallest nonzero frequency, set c1 = its symbol */
|
||||
/* In case of ties, take the larger symbol number */
|
||||
c1 = -1;
|
||||
v = 1000000000L;
|
||||
for (i = 0; i <= 256; i++) {
|
||||
if (freq[i] && freq[i] <= v) {
|
||||
v = freq[i];
|
||||
c1 = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the next smallest nonzero frequency, set c2 = its symbol */
|
||||
/* In case of ties, take the larger symbol number */
|
||||
c2 = -1;
|
||||
v = 1000000000L;
|
||||
for (i = 0; i <= 256; i++) {
|
||||
if (freq[i] && freq[i] <= v && i != c1) {
|
||||
v = freq[i];
|
||||
c2 = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Done if we've merged everything into one frequency */
|
||||
if (c2 < 0)
|
||||
break;
|
||||
|
||||
/* Else merge the two counts/trees */
|
||||
freq[c1] += freq[c2];
|
||||
freq[c2] = 0;
|
||||
|
||||
/* Increment the codesize of everything in c1's tree branch */
|
||||
codesize[c1]++;
|
||||
while (others[c1] >= 0) {
|
||||
c1 = others[c1];
|
||||
codesize[c1]++;
|
||||
}
|
||||
|
||||
others[c1] = c2; /* chain c2 onto c1's tree branch */
|
||||
|
||||
/* Increment the codesize of everything in c2's tree branch */
|
||||
codesize[c2]++;
|
||||
while (others[c2] >= 0) {
|
||||
c2 = others[c2];
|
||||
codesize[c2]++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now count the number of symbols of each code length */
|
||||
for (i = 0; i <= 256; i++) {
|
||||
if (codesize[i]) {
|
||||
/* The JPEG standard seems to think that this can't happen, */
|
||||
/* but I'm paranoid... */
|
||||
if (codesize[i] > MAX_CLEN)
|
||||
ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW);
|
||||
|
||||
bits[codesize[i]]++;
|
||||
}
|
||||
}
|
||||
|
||||
/* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure
|
||||
* Huffman procedure assigned any such lengths, we must adjust the coding.
|
||||
* Here is what the JPEG spec says about how this next bit works:
|
||||
* Since symbols are paired for the longest Huffman code, the symbols are
|
||||
* removed from this length category two at a time. The prefix for the pair
|
||||
* (which is one bit shorter) is allocated to one of the pair; then,
|
||||
* skipping the BITS entry for that prefix length, a code word from the next
|
||||
* shortest nonzero BITS entry is converted into a prefix for two code words
|
||||
* one bit longer.
|
||||
*/
|
||||
|
||||
for (i = MAX_CLEN; i > 16; i--) {
|
||||
while (bits[i] > 0) {
|
||||
j = i - 2; /* find length of new prefix to be used */
|
||||
while (bits[j] == 0)
|
||||
j--;
|
||||
|
||||
bits[i] -= 2; /* remove two symbols */
|
||||
bits[i-1]++; /* one goes in this length */
|
||||
bits[j+1] += 2; /* two new symbols in this length */
|
||||
bits[j]--; /* symbol of this length is now a prefix */
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the count for the pseudo-symbol 256 from the largest codelength */
|
||||
while (bits[i] == 0) /* find largest codelength still in use */
|
||||
i--;
|
||||
bits[i]--;
|
||||
|
||||
/* Return final symbol counts (only for lengths 0..16) */
|
||||
MEMCOPY(htbl->bits, bits, SIZEOF(htbl->bits));
|
||||
|
||||
/* Return a list of the symbols sorted by code length */
|
||||
/* It's not real clear to me why we don't need to consider the codelength
|
||||
* changes made above, but the JPEG spec seems to think this works.
|
||||
*/
|
||||
p = 0;
|
||||
for (i = 1; i <= MAX_CLEN; i++) {
|
||||
for (j = 0; j <= 255; j++) {
|
||||
if (codesize[j] == i) {
|
||||
htbl->huffval[p] = (UINT8) j;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set sent_table FALSE so updated table will be written to JPEG file. */
|
||||
htbl->sent_table = FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish up a statistics-gathering pass and create the new Huffman tables.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
finish_pass_gather (j_compress_ptr cinfo)
|
||||
{
|
||||
huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
|
||||
int ci, dctbl, actbl;
|
||||
jpeg_component_info * compptr;
|
||||
JHUFF_TBL **htblptr;
|
||||
boolean did_dc[NUM_HUFF_TBLS];
|
||||
boolean did_ac[NUM_HUFF_TBLS];
|
||||
|
||||
/* It's important not to apply jpeg_gen_optimal_table more than once
|
||||
* per table, because it clobbers the input frequency counts!
|
||||
*/
|
||||
MEMZERO(did_dc, SIZEOF(did_dc));
|
||||
MEMZERO(did_ac, SIZEOF(did_ac));
|
||||
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
dctbl = compptr->dc_tbl_no;
|
||||
actbl = compptr->ac_tbl_no;
|
||||
if (! did_dc[dctbl]) {
|
||||
htblptr = & cinfo->dc_huff_tbl_ptrs[dctbl];
|
||||
if (*htblptr == NULL)
|
||||
*htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo);
|
||||
jpeg_gen_optimal_table(cinfo, *htblptr, entropy->dc_count_ptrs[dctbl]);
|
||||
did_dc[dctbl] = TRUE;
|
||||
}
|
||||
if (! did_ac[actbl]) {
|
||||
htblptr = & cinfo->ac_huff_tbl_ptrs[actbl];
|
||||
if (*htblptr == NULL)
|
||||
*htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo);
|
||||
jpeg_gen_optimal_table(cinfo, *htblptr, entropy->ac_count_ptrs[actbl]);
|
||||
did_ac[actbl] = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* ENTROPY_OPT_SUPPORTED */
|
||||
|
||||
|
||||
/*
|
||||
* Module initialization routine for Huffman entropy encoding.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_huff_encoder (j_compress_ptr cinfo)
|
||||
{
|
||||
huff_entropy_ptr entropy;
|
||||
int i;
|
||||
|
||||
entropy = (huff_entropy_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(huff_entropy_encoder));
|
||||
cinfo->entropy = (struct jpeg_entropy_encoder *) entropy;
|
||||
entropy->pub.start_pass = start_pass_huff;
|
||||
|
||||
/* Mark tables unallocated */
|
||||
for (i = 0; i < NUM_HUFF_TBLS; i++) {
|
||||
entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL;
|
||||
#ifdef ENTROPY_OPT_SUPPORTED
|
||||
entropy->dc_count_ptrs[i] = entropy->ac_count_ptrs[i] = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
34
neo/renderer/jpeg-6/jchuff.h
Normal file
34
neo/renderer/jpeg-6/jchuff.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* jchuff.h
|
||||
*
|
||||
* Copyright (C) 1991-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains declarations for Huffman entropy encoding routines
|
||||
* that are shared between the sequential encoder (jchuff.c) and the
|
||||
* progressive encoder (jcphuff.c). No other modules need to see these.
|
||||
*/
|
||||
|
||||
/* Derived data constructed for each Huffman table */
|
||||
|
||||
typedef struct {
|
||||
unsigned int ehufco[256]; /* code for each symbol */
|
||||
char ehufsi[256]; /* length of code for each symbol */
|
||||
/* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */
|
||||
} c_derived_tbl;
|
||||
|
||||
/* Short forms of external names for systems with brain-damaged linkers. */
|
||||
|
||||
#ifdef NEED_SHORT_EXTERNAL_NAMES
|
||||
#define jpeg_make_c_derived_tbl jMkCDerived
|
||||
#define jpeg_gen_optimal_table jGenOptTbl
|
||||
#endif /* NEED_SHORT_EXTERNAL_NAMES */
|
||||
|
||||
/* Expand a Huffman table definition into the derived format */
|
||||
EXTERN void jpeg_make_c_derived_tbl JPP((j_compress_ptr cinfo,
|
||||
JHUFF_TBL * htbl, c_derived_tbl ** pdtbl));
|
||||
|
||||
/* Generate an optimal table definition given the specified counts */
|
||||
EXTERN void jpeg_gen_optimal_table JPP((j_compress_ptr cinfo,
|
||||
JHUFF_TBL * htbl, long freq[]));
|
||||
72
neo/renderer/jpeg-6/jcinit.c
Normal file
72
neo/renderer/jpeg-6/jcinit.c
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* jcinit.c
|
||||
*
|
||||
* Copyright (C) 1991-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains initialization logic for the JPEG compressor.
|
||||
* This routine is in charge of selecting the modules to be executed and
|
||||
* making an initialization call to each one.
|
||||
*
|
||||
* Logically, this code belongs in jcmaster.c. It's split out because
|
||||
* linking this routine implies linking the entire compression library.
|
||||
* For a transcoding-only application, we want to be able to use jcmaster.c
|
||||
* without linking in the whole library.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/*
|
||||
* Master selection of compression modules.
|
||||
* This is done once at the start of processing an image. We determine
|
||||
* which modules will be used and give them appropriate initialization calls.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_compress_master (j_compress_ptr cinfo)
|
||||
{
|
||||
/* Initialize master control (includes parameter checking/processing) */
|
||||
jinit_c_master_control(cinfo, FALSE /* full compression */);
|
||||
|
||||
/* Preprocessing */
|
||||
if (! cinfo->raw_data_in) {
|
||||
jinit_color_converter(cinfo);
|
||||
jinit_downsampler(cinfo);
|
||||
jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */);
|
||||
}
|
||||
/* Forward DCT */
|
||||
jinit_forward_dct(cinfo);
|
||||
/* Entropy encoding: either Huffman or arithmetic coding. */
|
||||
if (cinfo->arith_code) {
|
||||
ERREXIT(cinfo, JERR_ARITH_NOTIMPL);
|
||||
} else {
|
||||
if (cinfo->progressive_mode) {
|
||||
#ifdef C_PROGRESSIVE_SUPPORTED
|
||||
jinit_phuff_encoder(cinfo);
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
} else
|
||||
jinit_huff_encoder(cinfo);
|
||||
}
|
||||
|
||||
/* Need a full-image coefficient buffer in any multi-pass mode. */
|
||||
jinit_c_coef_controller(cinfo,
|
||||
(cinfo->num_scans > 1 || cinfo->optimize_coding));
|
||||
jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */);
|
||||
|
||||
jinit_marker_writer(cinfo);
|
||||
|
||||
/* We can now tell the memory manager to allocate virtual arrays. */
|
||||
(*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo);
|
||||
|
||||
/* Write the datastream header (SOI) immediately.
|
||||
* Frame and scan headers are postponed till later.
|
||||
* This lets application insert special markers after the SOI.
|
||||
*/
|
||||
(*cinfo->marker->write_file_header) (cinfo);
|
||||
}
|
||||
293
neo/renderer/jpeg-6/jcmainct.c
Normal file
293
neo/renderer/jpeg-6/jcmainct.c
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* jcmainct.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains the main buffer controller for compression.
|
||||
* The main buffer lies between the pre-processor and the JPEG
|
||||
* compressor proper; it holds downsampled data in the JPEG colorspace.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Note: currently, there is no operating mode in which a full-image buffer
|
||||
* is needed at this step. If there were, that mode could not be used with
|
||||
* "raw data" input, since this module is bypassed in that case. However,
|
||||
* we've left the code here for possible use in special applications.
|
||||
*/
|
||||
#undef FULL_MAIN_BUFFER_SUPPORTED
|
||||
|
||||
|
||||
/* Private buffer controller object */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_c_main_controller pub; /* public fields */
|
||||
|
||||
JDIMENSION cur_iMCU_row; /* number of current iMCU row */
|
||||
JDIMENSION rowgroup_ctr; /* counts row groups received in iMCU row */
|
||||
boolean suspended; /* remember if we suspended output */
|
||||
J_BUF_MODE pass_mode; /* current operating mode */
|
||||
|
||||
/* If using just a strip buffer, this points to the entire set of buffers
|
||||
* (we allocate one for each component). In the full-image case, this
|
||||
* points to the currently accessible strips of the virtual arrays.
|
||||
*/
|
||||
JSAMPARRAY buffer[MAX_COMPONENTS];
|
||||
|
||||
#ifdef FULL_MAIN_BUFFER_SUPPORTED
|
||||
/* If using full-image storage, this array holds pointers to virtual-array
|
||||
* control blocks for each component. Unused if not full-image storage.
|
||||
*/
|
||||
jvirt_sarray_ptr whole_image[MAX_COMPONENTS];
|
||||
#endif
|
||||
} my_main_controller;
|
||||
|
||||
typedef my_main_controller * my_main_ptr;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
METHODDEF void process_data_simple_main
|
||||
JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf,
|
||||
JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail));
|
||||
#ifdef FULL_MAIN_BUFFER_SUPPORTED
|
||||
METHODDEF void process_data_buffer_main
|
||||
JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf,
|
||||
JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail));
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a processing pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_main (j_compress_ptr cinfo, J_BUF_MODE pass_mode)
|
||||
{
|
||||
my_main_ptr main = (my_main_ptr) cinfo->main;
|
||||
|
||||
/* Do nothing in raw-data mode. */
|
||||
if (cinfo->raw_data_in)
|
||||
return;
|
||||
|
||||
main->cur_iMCU_row = 0; /* initialize counters */
|
||||
main->rowgroup_ctr = 0;
|
||||
main->suspended = FALSE;
|
||||
main->pass_mode = pass_mode; /* save mode for use by process_data */
|
||||
|
||||
switch (pass_mode) {
|
||||
case JBUF_PASS_THRU:
|
||||
#ifdef FULL_MAIN_BUFFER_SUPPORTED
|
||||
if (main->whole_image[0] != NULL)
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
#endif
|
||||
main->pub.process_data = process_data_simple_main;
|
||||
break;
|
||||
#ifdef FULL_MAIN_BUFFER_SUPPORTED
|
||||
case JBUF_SAVE_SOURCE:
|
||||
case JBUF_CRANK_DEST:
|
||||
case JBUF_SAVE_AND_PASS:
|
||||
if (main->whole_image[0] == NULL)
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
main->pub.process_data = process_data_buffer_main;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process some data.
|
||||
* This routine handles the simple pass-through mode,
|
||||
* where we have only a strip buffer.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
process_data_simple_main (j_compress_ptr cinfo,
|
||||
JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
|
||||
JDIMENSION in_rows_avail)
|
||||
{
|
||||
my_main_ptr main = (my_main_ptr) cinfo->main;
|
||||
|
||||
while (main->cur_iMCU_row < cinfo->total_iMCU_rows) {
|
||||
/* Read input data if we haven't filled the main buffer yet */
|
||||
if (main->rowgroup_ctr < DCTSIZE)
|
||||
(*cinfo->prep->pre_process_data) (cinfo,
|
||||
input_buf, in_row_ctr, in_rows_avail,
|
||||
main->buffer, &main->rowgroup_ctr,
|
||||
(JDIMENSION) DCTSIZE);
|
||||
|
||||
/* If we don't have a full iMCU row buffered, return to application for
|
||||
* more data. Note that preprocessor will always pad to fill the iMCU row
|
||||
* at the bottom of the image.
|
||||
*/
|
||||
if (main->rowgroup_ctr != DCTSIZE)
|
||||
return;
|
||||
|
||||
/* Send the completed row to the compressor */
|
||||
if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) {
|
||||
/* If compressor did not consume the whole row, then we must need to
|
||||
* suspend processing and return to the application. In this situation
|
||||
* we pretend we didn't yet consume the last input row; otherwise, if
|
||||
* it happened to be the last row of the image, the application would
|
||||
* think we were done.
|
||||
*/
|
||||
if (! main->suspended) {
|
||||
(*in_row_ctr)--;
|
||||
main->suspended = TRUE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* We did finish the row. Undo our little suspension hack if a previous
|
||||
* call suspended; then mark the main buffer empty.
|
||||
*/
|
||||
if (main->suspended) {
|
||||
(*in_row_ctr)++;
|
||||
main->suspended = FALSE;
|
||||
}
|
||||
main->rowgroup_ctr = 0;
|
||||
main->cur_iMCU_row++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef FULL_MAIN_BUFFER_SUPPORTED
|
||||
|
||||
/*
|
||||
* Process some data.
|
||||
* This routine handles all of the modes that use a full-size buffer.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
process_data_buffer_main (j_compress_ptr cinfo,
|
||||
JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
|
||||
JDIMENSION in_rows_avail)
|
||||
{
|
||||
my_main_ptr main = (my_main_ptr) cinfo->main;
|
||||
int ci;
|
||||
jpeg_component_info *compptr;
|
||||
boolean writing = (main->pass_mode != JBUF_CRANK_DEST);
|
||||
|
||||
while (main->cur_iMCU_row < cinfo->total_iMCU_rows) {
|
||||
/* Realign the virtual buffers if at the start of an iMCU row. */
|
||||
if (main->rowgroup_ctr == 0) {
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
main->buffer[ci] = (*cinfo->mem->access_virt_sarray)
|
||||
((j_common_ptr) cinfo, main->whole_image[ci],
|
||||
main->cur_iMCU_row * (compptr->v_samp_factor * DCTSIZE),
|
||||
(JDIMENSION) (compptr->v_samp_factor * DCTSIZE), writing);
|
||||
}
|
||||
/* In a read pass, pretend we just read some source data. */
|
||||
if (! writing) {
|
||||
*in_row_ctr += cinfo->max_v_samp_factor * DCTSIZE;
|
||||
main->rowgroup_ctr = DCTSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If a write pass, read input data until the current iMCU row is full. */
|
||||
/* Note: preprocessor will pad if necessary to fill the last iMCU row. */
|
||||
if (writing) {
|
||||
(*cinfo->prep->pre_process_data) (cinfo,
|
||||
input_buf, in_row_ctr, in_rows_avail,
|
||||
main->buffer, &main->rowgroup_ctr,
|
||||
(JDIMENSION) DCTSIZE);
|
||||
/* Return to application if we need more data to fill the iMCU row. */
|
||||
if (main->rowgroup_ctr < DCTSIZE)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Emit data, unless this is a sink-only pass. */
|
||||
if (main->pass_mode != JBUF_SAVE_SOURCE) {
|
||||
if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) {
|
||||
/* If compressor did not consume the whole row, then we must need to
|
||||
* suspend processing and return to the application. In this situation
|
||||
* we pretend we didn't yet consume the last input row; otherwise, if
|
||||
* it happened to be the last row of the image, the application would
|
||||
* think we were done.
|
||||
*/
|
||||
if (! main->suspended) {
|
||||
(*in_row_ctr)--;
|
||||
main->suspended = TRUE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* We did finish the row. Undo our little suspension hack if a previous
|
||||
* call suspended; then mark the main buffer empty.
|
||||
*/
|
||||
if (main->suspended) {
|
||||
(*in_row_ctr)++;
|
||||
main->suspended = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If get here, we are done with this iMCU row. Mark buffer empty. */
|
||||
main->rowgroup_ctr = 0;
|
||||
main->cur_iMCU_row++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* FULL_MAIN_BUFFER_SUPPORTED */
|
||||
|
||||
|
||||
/*
|
||||
* Initialize main buffer controller.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer)
|
||||
{
|
||||
my_main_ptr main;
|
||||
int ci;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
main = (my_main_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_main_controller));
|
||||
cinfo->main = (struct jpeg_c_main_controller *) main;
|
||||
main->pub.start_pass = start_pass_main;
|
||||
|
||||
/* We don't need to create a buffer in raw-data mode. */
|
||||
if (cinfo->raw_data_in)
|
||||
return;
|
||||
|
||||
/* Create the buffer. It holds downsampled data, so each component
|
||||
* may be of a different size.
|
||||
*/
|
||||
if (need_full_buffer) {
|
||||
#ifdef FULL_MAIN_BUFFER_SUPPORTED
|
||||
/* Allocate a full-image virtual array for each component */
|
||||
/* Note we pad the bottom to a multiple of the iMCU height */
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
main->whole_image[ci] = (*cinfo->mem->request_virt_sarray)
|
||||
((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
|
||||
compptr->width_in_blocks * DCTSIZE,
|
||||
(JDIMENSION) jround_up((long) compptr->height_in_blocks,
|
||||
(long) compptr->v_samp_factor) * DCTSIZE,
|
||||
(JDIMENSION) (compptr->v_samp_factor * DCTSIZE));
|
||||
}
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef FULL_MAIN_BUFFER_SUPPORTED
|
||||
main->whole_image[0] = NULL; /* flag for no virtual arrays */
|
||||
#endif
|
||||
/* Allocate a strip buffer for each component */
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
main->buffer[ci] = (*cinfo->mem->alloc_sarray)
|
||||
((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
compptr->width_in_blocks * DCTSIZE,
|
||||
(JDIMENSION) (compptr->v_samp_factor * DCTSIZE));
|
||||
}
|
||||
}
|
||||
}
|
||||
639
neo/renderer/jpeg-6/jcmarker.c
Normal file
639
neo/renderer/jpeg-6/jcmarker.c
Normal file
@@ -0,0 +1,639 @@
|
||||
/*
|
||||
* jcmarker.c
|
||||
*
|
||||
* Copyright (C) 1991-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains routines to write JPEG datastream markers.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
typedef enum { /* JPEG marker codes */
|
||||
M_SOF0 = 0xc0,
|
||||
M_SOF1 = 0xc1,
|
||||
M_SOF2 = 0xc2,
|
||||
M_SOF3 = 0xc3,
|
||||
|
||||
M_SOF5 = 0xc5,
|
||||
M_SOF6 = 0xc6,
|
||||
M_SOF7 = 0xc7,
|
||||
|
||||
M_JPG = 0xc8,
|
||||
M_SOF9 = 0xc9,
|
||||
M_SOF10 = 0xca,
|
||||
M_SOF11 = 0xcb,
|
||||
|
||||
M_SOF13 = 0xcd,
|
||||
M_SOF14 = 0xce,
|
||||
M_SOF15 = 0xcf,
|
||||
|
||||
M_DHT = 0xc4,
|
||||
|
||||
M_DAC = 0xcc,
|
||||
|
||||
M_RST0 = 0xd0,
|
||||
M_RST1 = 0xd1,
|
||||
M_RST2 = 0xd2,
|
||||
M_RST3 = 0xd3,
|
||||
M_RST4 = 0xd4,
|
||||
M_RST5 = 0xd5,
|
||||
M_RST6 = 0xd6,
|
||||
M_RST7 = 0xd7,
|
||||
|
||||
M_SOI = 0xd8,
|
||||
M_EOI = 0xd9,
|
||||
M_SOS = 0xda,
|
||||
M_DQT = 0xdb,
|
||||
M_DNL = 0xdc,
|
||||
M_DRI = 0xdd,
|
||||
M_DHP = 0xde,
|
||||
M_EXP = 0xdf,
|
||||
|
||||
M_APP0 = 0xe0,
|
||||
M_APP1 = 0xe1,
|
||||
M_APP2 = 0xe2,
|
||||
M_APP3 = 0xe3,
|
||||
M_APP4 = 0xe4,
|
||||
M_APP5 = 0xe5,
|
||||
M_APP6 = 0xe6,
|
||||
M_APP7 = 0xe7,
|
||||
M_APP8 = 0xe8,
|
||||
M_APP9 = 0xe9,
|
||||
M_APP10 = 0xea,
|
||||
M_APP11 = 0xeb,
|
||||
M_APP12 = 0xec,
|
||||
M_APP13 = 0xed,
|
||||
M_APP14 = 0xee,
|
||||
M_APP15 = 0xef,
|
||||
|
||||
M_JPG0 = 0xf0,
|
||||
M_JPG13 = 0xfd,
|
||||
M_COM = 0xfe,
|
||||
|
||||
M_TEM = 0x01,
|
||||
|
||||
M_ERROR = 0x100
|
||||
} JPEG_MARKER;
|
||||
|
||||
|
||||
/*
|
||||
* Basic output routines.
|
||||
*
|
||||
* Note that we do not support suspension while writing a marker.
|
||||
* Therefore, an application using suspension must ensure that there is
|
||||
* enough buffer space for the initial markers (typ. 600-700 bytes) before
|
||||
* calling jpeg_start_compress, and enough space to write the trailing EOI
|
||||
* (a few bytes) before calling jpeg_finish_compress. Multipass compression
|
||||
* modes are not supported at all with suspension, so those two are the only
|
||||
* points where markers will be written.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
emit_byte (j_compress_ptr cinfo, int val)
|
||||
/* Emit a byte */
|
||||
{
|
||||
struct jpeg_destination_mgr * dest = cinfo->dest;
|
||||
|
||||
*(dest->next_output_byte)++ = (JOCTET) val;
|
||||
if (--dest->free_in_buffer == 0) {
|
||||
if (! (*dest->empty_output_buffer) (cinfo))
|
||||
ERREXIT(cinfo, JERR_CANT_SUSPEND);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
emit_marker (j_compress_ptr cinfo, JPEG_MARKER mark)
|
||||
/* Emit a marker code */
|
||||
{
|
||||
emit_byte(cinfo, 0xFF);
|
||||
emit_byte(cinfo, (int) mark);
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
emit_2bytes (j_compress_ptr cinfo, int value)
|
||||
/* Emit a 2-byte integer; these are always MSB first in JPEG files */
|
||||
{
|
||||
emit_byte(cinfo, (value >> 8) & 0xFF);
|
||||
emit_byte(cinfo, value & 0xFF);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Routines to write specific marker types.
|
||||
*/
|
||||
|
||||
LOCAL int
|
||||
emit_dqt (j_compress_ptr cinfo, int index)
|
||||
/* Emit a DQT marker */
|
||||
/* Returns the precision used (0 = 8bits, 1 = 16bits) for baseline checking */
|
||||
{
|
||||
JQUANT_TBL * qtbl = cinfo->quant_tbl_ptrs[index];
|
||||
int prec;
|
||||
int i;
|
||||
|
||||
if (qtbl == NULL)
|
||||
ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, index);
|
||||
|
||||
prec = 0;
|
||||
for (i = 0; i < DCTSIZE2; i++) {
|
||||
if (qtbl->quantval[i] > 255)
|
||||
prec = 1;
|
||||
}
|
||||
|
||||
if (! qtbl->sent_table) {
|
||||
emit_marker(cinfo, M_DQT);
|
||||
|
||||
emit_2bytes(cinfo, prec ? DCTSIZE2*2 + 1 + 2 : DCTSIZE2 + 1 + 2);
|
||||
|
||||
emit_byte(cinfo, index + (prec<<4));
|
||||
|
||||
for (i = 0; i < DCTSIZE2; i++) {
|
||||
if (prec)
|
||||
emit_byte(cinfo, qtbl->quantval[i] >> 8);
|
||||
emit_byte(cinfo, qtbl->quantval[i] & 0xFF);
|
||||
}
|
||||
|
||||
qtbl->sent_table = TRUE;
|
||||
}
|
||||
|
||||
return prec;
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
emit_dht (j_compress_ptr cinfo, int index, boolean is_ac)
|
||||
/* Emit a DHT marker */
|
||||
{
|
||||
JHUFF_TBL * htbl;
|
||||
int length, i;
|
||||
|
||||
if (is_ac) {
|
||||
htbl = cinfo->ac_huff_tbl_ptrs[index];
|
||||
index += 0x10; /* output index has AC bit set */
|
||||
} else {
|
||||
htbl = cinfo->dc_huff_tbl_ptrs[index];
|
||||
}
|
||||
|
||||
if (htbl == NULL)
|
||||
ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, index);
|
||||
|
||||
if (! htbl->sent_table) {
|
||||
emit_marker(cinfo, M_DHT);
|
||||
|
||||
length = 0;
|
||||
for (i = 1; i <= 16; i++)
|
||||
length += htbl->bits[i];
|
||||
|
||||
emit_2bytes(cinfo, length + 2 + 1 + 16);
|
||||
emit_byte(cinfo, index);
|
||||
|
||||
for (i = 1; i <= 16; i++)
|
||||
emit_byte(cinfo, htbl->bits[i]);
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
emit_byte(cinfo, htbl->huffval[i]);
|
||||
|
||||
htbl->sent_table = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
emit_dac (j_compress_ptr cinfo)
|
||||
/* Emit a DAC marker */
|
||||
/* Since the useful info is so small, we want to emit all the tables in */
|
||||
/* one DAC marker. Therefore this routine does its own scan of the table. */
|
||||
{
|
||||
#ifdef C_ARITH_CODING_SUPPORTED
|
||||
char dc_in_use[NUM_ARITH_TBLS];
|
||||
char ac_in_use[NUM_ARITH_TBLS];
|
||||
int length, i;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
for (i = 0; i < NUM_ARITH_TBLS; i++)
|
||||
dc_in_use[i] = ac_in_use[i] = 0;
|
||||
|
||||
for (i = 0; i < cinfo->comps_in_scan; i++) {
|
||||
compptr = cinfo->cur_comp_info[i];
|
||||
dc_in_use[compptr->dc_tbl_no] = 1;
|
||||
ac_in_use[compptr->ac_tbl_no] = 1;
|
||||
}
|
||||
|
||||
length = 0;
|
||||
for (i = 0; i < NUM_ARITH_TBLS; i++)
|
||||
length += dc_in_use[i] + ac_in_use[i];
|
||||
|
||||
emit_marker(cinfo, M_DAC);
|
||||
|
||||
emit_2bytes(cinfo, length*2 + 2);
|
||||
|
||||
for (i = 0; i < NUM_ARITH_TBLS; i++) {
|
||||
if (dc_in_use[i]) {
|
||||
emit_byte(cinfo, i);
|
||||
emit_byte(cinfo, cinfo->arith_dc_L[i] + (cinfo->arith_dc_U[i]<<4));
|
||||
}
|
||||
if (ac_in_use[i]) {
|
||||
emit_byte(cinfo, i + 0x10);
|
||||
emit_byte(cinfo, cinfo->arith_ac_K[i]);
|
||||
}
|
||||
}
|
||||
#endif /* C_ARITH_CODING_SUPPORTED */
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
emit_dri (j_compress_ptr cinfo)
|
||||
/* Emit a DRI marker */
|
||||
{
|
||||
emit_marker(cinfo, M_DRI);
|
||||
|
||||
emit_2bytes(cinfo, 4); /* fixed length */
|
||||
|
||||
emit_2bytes(cinfo, (int) cinfo->restart_interval);
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
emit_sof (j_compress_ptr cinfo, JPEG_MARKER code)
|
||||
/* Emit a SOF marker */
|
||||
{
|
||||
int ci;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
emit_marker(cinfo, code);
|
||||
|
||||
emit_2bytes(cinfo, 3 * cinfo->num_components + 2 + 5 + 1); /* length */
|
||||
|
||||
/* Make sure image isn't bigger than SOF field can handle */
|
||||
if ((long) cinfo->image_height > 65535L ||
|
||||
(long) cinfo->image_width > 65535L)
|
||||
ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) 65535);
|
||||
|
||||
emit_byte(cinfo, cinfo->data_precision);
|
||||
emit_2bytes(cinfo, (int) cinfo->image_height);
|
||||
emit_2bytes(cinfo, (int) cinfo->image_width);
|
||||
|
||||
emit_byte(cinfo, cinfo->num_components);
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
emit_byte(cinfo, compptr->component_id);
|
||||
emit_byte(cinfo, (compptr->h_samp_factor << 4) + compptr->v_samp_factor);
|
||||
emit_byte(cinfo, compptr->quant_tbl_no);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
emit_sos (j_compress_ptr cinfo)
|
||||
/* Emit a SOS marker */
|
||||
{
|
||||
int i, td, ta;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
emit_marker(cinfo, M_SOS);
|
||||
|
||||
emit_2bytes(cinfo, 2 * cinfo->comps_in_scan + 2 + 1 + 3); /* length */
|
||||
|
||||
emit_byte(cinfo, cinfo->comps_in_scan);
|
||||
|
||||
for (i = 0; i < cinfo->comps_in_scan; i++) {
|
||||
compptr = cinfo->cur_comp_info[i];
|
||||
emit_byte(cinfo, compptr->component_id);
|
||||
td = compptr->dc_tbl_no;
|
||||
ta = compptr->ac_tbl_no;
|
||||
if (cinfo->progressive_mode) {
|
||||
/* Progressive mode: only DC or only AC tables are used in one scan;
|
||||
* furthermore, Huffman coding of DC refinement uses no table at all.
|
||||
* We emit 0 for unused field(s); this is recommended by the P&M text
|
||||
* but does not seem to be specified in the standard.
|
||||
*/
|
||||
if (cinfo->Ss == 0) {
|
||||
ta = 0; /* DC scan */
|
||||
if (cinfo->Ah != 0 && !cinfo->arith_code)
|
||||
td = 0; /* no DC table either */
|
||||
} else {
|
||||
td = 0; /* AC scan */
|
||||
}
|
||||
}
|
||||
emit_byte(cinfo, (td << 4) + ta);
|
||||
}
|
||||
|
||||
emit_byte(cinfo, cinfo->Ss);
|
||||
emit_byte(cinfo, cinfo->Se);
|
||||
emit_byte(cinfo, (cinfo->Ah << 4) + cinfo->Al);
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
emit_jfif_app0 (j_compress_ptr cinfo)
|
||||
/* Emit a JFIF-compliant APP0 marker */
|
||||
{
|
||||
/*
|
||||
* Length of APP0 block (2 bytes)
|
||||
* Block ID (4 bytes - ASCII "JFIF")
|
||||
* Zero byte (1 byte to terminate the ID string)
|
||||
* Version Major, Minor (2 bytes - 0x01, 0x01)
|
||||
* Units (1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm)
|
||||
* Xdpu (2 bytes - dots per unit horizontal)
|
||||
* Ydpu (2 bytes - dots per unit vertical)
|
||||
* Thumbnail X size (1 byte)
|
||||
* Thumbnail Y size (1 byte)
|
||||
*/
|
||||
|
||||
emit_marker(cinfo, M_APP0);
|
||||
|
||||
emit_2bytes(cinfo, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */
|
||||
|
||||
emit_byte(cinfo, 0x4A); /* Identifier: ASCII "JFIF" */
|
||||
emit_byte(cinfo, 0x46);
|
||||
emit_byte(cinfo, 0x49);
|
||||
emit_byte(cinfo, 0x46);
|
||||
emit_byte(cinfo, 0);
|
||||
/* We currently emit version code 1.01 since we use no 1.02 features.
|
||||
* This may avoid complaints from some older decoders.
|
||||
*/
|
||||
emit_byte(cinfo, 1); /* Major version */
|
||||
emit_byte(cinfo, 1); /* Minor version */
|
||||
emit_byte(cinfo, cinfo->density_unit); /* Pixel size information */
|
||||
emit_2bytes(cinfo, (int) cinfo->X_density);
|
||||
emit_2bytes(cinfo, (int) cinfo->Y_density);
|
||||
emit_byte(cinfo, 0); /* No thumbnail image */
|
||||
emit_byte(cinfo, 0);
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
emit_adobe_app14 (j_compress_ptr cinfo)
|
||||
/* Emit an Adobe APP14 marker */
|
||||
{
|
||||
/*
|
||||
* Length of APP14 block (2 bytes)
|
||||
* Block ID (5 bytes - ASCII "Adobe")
|
||||
* Version Number (2 bytes - currently 100)
|
||||
* Flags0 (2 bytes - currently 0)
|
||||
* Flags1 (2 bytes - currently 0)
|
||||
* Color transform (1 byte)
|
||||
*
|
||||
* Although Adobe TN 5116 mentions Version = 101, all the Adobe files
|
||||
* now in circulation seem to use Version = 100, so that's what we write.
|
||||
*
|
||||
* We write the color transform byte as 1 if the JPEG color space is
|
||||
* YCbCr, 2 if it's YCCK, 0 otherwise. Adobe's definition has to do with
|
||||
* whether the encoder performed a transformation, which is pretty useless.
|
||||
*/
|
||||
|
||||
emit_marker(cinfo, M_APP14);
|
||||
|
||||
emit_2bytes(cinfo, 2 + 5 + 2 + 2 + 2 + 1); /* length */
|
||||
|
||||
emit_byte(cinfo, 0x41); /* Identifier: ASCII "Adobe" */
|
||||
emit_byte(cinfo, 0x64);
|
||||
emit_byte(cinfo, 0x6F);
|
||||
emit_byte(cinfo, 0x62);
|
||||
emit_byte(cinfo, 0x65);
|
||||
emit_2bytes(cinfo, 100); /* Version */
|
||||
emit_2bytes(cinfo, 0); /* Flags0 */
|
||||
emit_2bytes(cinfo, 0); /* Flags1 */
|
||||
switch (cinfo->jpeg_color_space) {
|
||||
case JCS_YCbCr:
|
||||
emit_byte(cinfo, 1); /* Color transform = 1 */
|
||||
break;
|
||||
case JCS_YCCK:
|
||||
emit_byte(cinfo, 2); /* Color transform = 2 */
|
||||
break;
|
||||
default:
|
||||
emit_byte(cinfo, 0); /* Color transform = 0 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This routine is exported for possible use by applications.
|
||||
* The intended use is to emit COM or APPn markers after calling
|
||||
* jpeg_start_compress() and before the first jpeg_write_scanlines() call
|
||||
* (hence, after write_file_header but before write_frame_header).
|
||||
* Other uses are not guaranteed to produce desirable results.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
write_any_marker (j_compress_ptr cinfo, int marker,
|
||||
const JOCTET *dataptr, unsigned int datalen)
|
||||
/* Emit an arbitrary marker with parameters */
|
||||
{
|
||||
if (datalen <= (unsigned int) 65533) { /* safety check */
|
||||
emit_marker(cinfo, (JPEG_MARKER) marker);
|
||||
|
||||
emit_2bytes(cinfo, (int) (datalen + 2)); /* total length */
|
||||
|
||||
while (datalen--) {
|
||||
emit_byte(cinfo, *dataptr);
|
||||
dataptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write datastream header.
|
||||
* This consists of an SOI and optional APPn markers.
|
||||
* We recommend use of the JFIF marker, but not the Adobe marker,
|
||||
* when using YCbCr or grayscale data. The JFIF marker should NOT
|
||||
* be used for any other JPEG colorspace. The Adobe marker is helpful
|
||||
* to distinguish RGB, CMYK, and YCCK colorspaces.
|
||||
* Note that an application can write additional header markers after
|
||||
* jpeg_start_compress returns.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
write_file_header (j_compress_ptr cinfo)
|
||||
{
|
||||
emit_marker(cinfo, M_SOI); /* first the SOI */
|
||||
|
||||
if (cinfo->write_JFIF_header) /* next an optional JFIF APP0 */
|
||||
emit_jfif_app0(cinfo);
|
||||
if (cinfo->write_Adobe_marker) /* next an optional Adobe APP14 */
|
||||
emit_adobe_app14(cinfo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write frame header.
|
||||
* This consists of DQT and SOFn markers.
|
||||
* Note that we do not emit the SOF until we have emitted the DQT(s).
|
||||
* This avoids compatibility problems with incorrect implementations that
|
||||
* try to error-check the quant table numbers as soon as they see the SOF.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
write_frame_header (j_compress_ptr cinfo)
|
||||
{
|
||||
int ci, prec;
|
||||
boolean is_baseline;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
/* Emit DQT for each quantization table.
|
||||
* Note that emit_dqt() suppresses any duplicate tables.
|
||||
*/
|
||||
prec = 0;
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
prec += emit_dqt(cinfo, compptr->quant_tbl_no);
|
||||
}
|
||||
/* now prec is nonzero iff there are any 16-bit quant tables. */
|
||||
|
||||
/* Check for a non-baseline specification.
|
||||
* Note we assume that Huffman table numbers won't be changed later.
|
||||
*/
|
||||
if (cinfo->arith_code || cinfo->progressive_mode ||
|
||||
cinfo->data_precision != 8) {
|
||||
is_baseline = FALSE;
|
||||
} else {
|
||||
is_baseline = TRUE;
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
if (compptr->dc_tbl_no > 1 || compptr->ac_tbl_no > 1)
|
||||
is_baseline = FALSE;
|
||||
}
|
||||
if (prec && is_baseline) {
|
||||
is_baseline = FALSE;
|
||||
/* If it's baseline except for quantizer size, warn the user */
|
||||
TRACEMS(cinfo, 0, JTRC_16BIT_TABLES);
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit the proper SOF marker */
|
||||
if (cinfo->arith_code) {
|
||||
emit_sof(cinfo, M_SOF9); /* SOF code for arithmetic coding */
|
||||
} else {
|
||||
if (cinfo->progressive_mode)
|
||||
emit_sof(cinfo, M_SOF2); /* SOF code for progressive Huffman */
|
||||
else if (is_baseline)
|
||||
emit_sof(cinfo, M_SOF0); /* SOF code for baseline implementation */
|
||||
else
|
||||
emit_sof(cinfo, M_SOF1); /* SOF code for non-baseline Huffman file */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write scan header.
|
||||
* This consists of DHT or DAC markers, optional DRI, and SOS.
|
||||
* Compressed data will be written following the SOS.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
write_scan_header (j_compress_ptr cinfo)
|
||||
{
|
||||
int i;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
if (cinfo->arith_code) {
|
||||
/* Emit arith conditioning info. We may have some duplication
|
||||
* if the file has multiple scans, but it's so small it's hardly
|
||||
* worth worrying about.
|
||||
*/
|
||||
emit_dac(cinfo);
|
||||
} else {
|
||||
/* Emit Huffman tables.
|
||||
* Note that emit_dht() suppresses any duplicate tables.
|
||||
*/
|
||||
for (i = 0; i < cinfo->comps_in_scan; i++) {
|
||||
compptr = cinfo->cur_comp_info[i];
|
||||
if (cinfo->progressive_mode) {
|
||||
/* Progressive mode: only DC or only AC tables are used in one scan */
|
||||
if (cinfo->Ss == 0) {
|
||||
if (cinfo->Ah == 0) /* DC needs no table for refinement scan */
|
||||
emit_dht(cinfo, compptr->dc_tbl_no, FALSE);
|
||||
} else {
|
||||
emit_dht(cinfo, compptr->ac_tbl_no, TRUE);
|
||||
}
|
||||
} else {
|
||||
/* Sequential mode: need both DC and AC tables */
|
||||
emit_dht(cinfo, compptr->dc_tbl_no, FALSE);
|
||||
emit_dht(cinfo, compptr->ac_tbl_no, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit DRI if required --- note that DRI value could change for each scan.
|
||||
* If it doesn't, a tiny amount of space is wasted in multiple-scan files.
|
||||
* We assume DRI will never be nonzero for one scan and zero for a later one.
|
||||
*/
|
||||
if (cinfo->restart_interval)
|
||||
emit_dri(cinfo);
|
||||
|
||||
emit_sos(cinfo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write datastream trailer.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
write_file_trailer (j_compress_ptr cinfo)
|
||||
{
|
||||
emit_marker(cinfo, M_EOI);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write an abbreviated table-specification datastream.
|
||||
* This consists of SOI, DQT and DHT tables, and EOI.
|
||||
* Any table that is defined and not marked sent_table = TRUE will be
|
||||
* emitted. Note that all tables will be marked sent_table = TRUE at exit.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
write_tables_only (j_compress_ptr cinfo)
|
||||
{
|
||||
int i;
|
||||
|
||||
emit_marker(cinfo, M_SOI);
|
||||
|
||||
for (i = 0; i < NUM_QUANT_TBLS; i++) {
|
||||
if (cinfo->quant_tbl_ptrs[i] != NULL)
|
||||
(void) emit_dqt(cinfo, i);
|
||||
}
|
||||
|
||||
if (! cinfo->arith_code) {
|
||||
for (i = 0; i < NUM_HUFF_TBLS; i++) {
|
||||
if (cinfo->dc_huff_tbl_ptrs[i] != NULL)
|
||||
emit_dht(cinfo, i, FALSE);
|
||||
if (cinfo->ac_huff_tbl_ptrs[i] != NULL)
|
||||
emit_dht(cinfo, i, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
emit_marker(cinfo, M_EOI);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the marker writer module.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_marker_writer (j_compress_ptr cinfo)
|
||||
{
|
||||
/* Create the subobject */
|
||||
cinfo->marker = (struct jpeg_marker_writer *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(struct jpeg_marker_writer));
|
||||
/* Initialize method pointers */
|
||||
cinfo->marker->write_any_marker = write_any_marker;
|
||||
cinfo->marker->write_file_header = write_file_header;
|
||||
cinfo->marker->write_frame_header = write_frame_header;
|
||||
cinfo->marker->write_scan_header = write_scan_header;
|
||||
cinfo->marker->write_file_trailer = write_file_trailer;
|
||||
cinfo->marker->write_tables_only = write_tables_only;
|
||||
}
|
||||
578
neo/renderer/jpeg-6/jcmaster.c
Normal file
578
neo/renderer/jpeg-6/jcmaster.c
Normal file
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
* jcmaster.c
|
||||
*
|
||||
* Copyright (C) 1991-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains master control logic for the JPEG compressor.
|
||||
* These routines are concerned with parameter validation, initial setup,
|
||||
* and inter-pass control (determining the number of passes and the work
|
||||
* to be done in each pass).
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Private state */
|
||||
|
||||
typedef enum {
|
||||
main_pass, /* input data, also do first output step */
|
||||
huff_opt_pass, /* Huffman code optimization pass */
|
||||
output_pass /* data output pass */
|
||||
} c_pass_type;
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_comp_master pub; /* public fields */
|
||||
|
||||
c_pass_type pass_type; /* the type of the current pass */
|
||||
|
||||
int pass_number; /* # of passes completed */
|
||||
int total_passes; /* total # of passes needed */
|
||||
|
||||
int scan_number; /* current index in scan_info[] */
|
||||
} my_comp_master;
|
||||
|
||||
typedef my_comp_master * my_master_ptr;
|
||||
|
||||
|
||||
/*
|
||||
* Support routines that do various essential calculations.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
initial_setup (j_compress_ptr cinfo)
|
||||
/* Do computations that are needed before master selection phase */
|
||||
{
|
||||
int ci;
|
||||
jpeg_component_info *compptr;
|
||||
long samplesperrow;
|
||||
JDIMENSION jd_samplesperrow;
|
||||
|
||||
/* Sanity check on image dimensions */
|
||||
if (cinfo->image_height <= 0 || cinfo->image_width <= 0
|
||||
|| cinfo->num_components <= 0 || cinfo->input_components <= 0)
|
||||
ERREXIT(cinfo, JERR_EMPTY_IMAGE);
|
||||
|
||||
/* Make sure image isn't bigger than I can handle */
|
||||
if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION ||
|
||||
(long) cinfo->image_width > (long) JPEG_MAX_DIMENSION)
|
||||
ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION);
|
||||
|
||||
/* Width of an input scanline must be representable as JDIMENSION. */
|
||||
samplesperrow = (long) cinfo->image_width * (long) cinfo->input_components;
|
||||
jd_samplesperrow = (JDIMENSION) samplesperrow;
|
||||
if ((long) jd_samplesperrow != samplesperrow)
|
||||
ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
|
||||
|
||||
/* For now, precision must match compiled-in value... */
|
||||
if (cinfo->data_precision != BITS_IN_JSAMPLE)
|
||||
ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision);
|
||||
|
||||
/* Check that number of components won't exceed internal array sizes */
|
||||
if (cinfo->num_components > MAX_COMPONENTS)
|
||||
ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components,
|
||||
MAX_COMPONENTS);
|
||||
|
||||
/* Compute maximum sampling factors; check factor validity */
|
||||
cinfo->max_h_samp_factor = 1;
|
||||
cinfo->max_v_samp_factor = 1;
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR ||
|
||||
compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR)
|
||||
ERREXIT(cinfo, JERR_BAD_SAMPLING);
|
||||
cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor,
|
||||
compptr->h_samp_factor);
|
||||
cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor,
|
||||
compptr->v_samp_factor);
|
||||
}
|
||||
|
||||
/* Compute dimensions of components */
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* Fill in the correct component_index value; don't rely on application */
|
||||
compptr->component_index = ci;
|
||||
/* For compression, we never do DCT scaling. */
|
||||
compptr->DCT_scaled_size = DCTSIZE;
|
||||
/* Size in DCT blocks */
|
||||
compptr->width_in_blocks = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor,
|
||||
(long) (cinfo->max_h_samp_factor * DCTSIZE));
|
||||
compptr->height_in_blocks = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor,
|
||||
(long) (cinfo->max_v_samp_factor * DCTSIZE));
|
||||
/* Size in samples */
|
||||
compptr->downsampled_width = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor,
|
||||
(long) cinfo->max_h_samp_factor);
|
||||
compptr->downsampled_height = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor,
|
||||
(long) cinfo->max_v_samp_factor);
|
||||
/* Mark component needed (this flag isn't actually used for compression) */
|
||||
compptr->component_needed = TRUE;
|
||||
}
|
||||
|
||||
/* Compute number of fully interleaved MCU rows (number of times that
|
||||
* main controller will call coefficient controller).
|
||||
*/
|
||||
cinfo->total_iMCU_rows = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height,
|
||||
(long) (cinfo->max_v_samp_factor*DCTSIZE));
|
||||
}
|
||||
|
||||
|
||||
#ifdef C_MULTISCAN_FILES_SUPPORTED
|
||||
|
||||
LOCAL void
|
||||
validate_script (j_compress_ptr cinfo)
|
||||
/* Verify that the scan script in cinfo->scan_info[] is valid; also
|
||||
* determine whether it uses progressive JPEG, and set cinfo->progressive_mode.
|
||||
*/
|
||||
{
|
||||
const jpeg_scan_info * scanptr;
|
||||
int scanno, ncomps, ci, coefi, thisi;
|
||||
int Ss, Se, Ah, Al;
|
||||
boolean component_sent[MAX_COMPONENTS];
|
||||
#ifdef C_PROGRESSIVE_SUPPORTED
|
||||
int * last_bitpos_ptr;
|
||||
int last_bitpos[MAX_COMPONENTS][DCTSIZE2];
|
||||
/* -1 until that coefficient has been seen; then last Al for it */
|
||||
#endif
|
||||
|
||||
if (cinfo->num_scans <= 0)
|
||||
ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, 0);
|
||||
|
||||
/* For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1;
|
||||
* for progressive JPEG, no scan can have this.
|
||||
*/
|
||||
scanptr = cinfo->scan_info;
|
||||
if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2-1) {
|
||||
#ifdef C_PROGRESSIVE_SUPPORTED
|
||||
cinfo->progressive_mode = TRUE;
|
||||
last_bitpos_ptr = & last_bitpos[0][0];
|
||||
for (ci = 0; ci < cinfo->num_components; ci++)
|
||||
for (coefi = 0; coefi < DCTSIZE2; coefi++)
|
||||
*last_bitpos_ptr++ = -1;
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
} else {
|
||||
cinfo->progressive_mode = FALSE;
|
||||
for (ci = 0; ci < cinfo->num_components; ci++)
|
||||
component_sent[ci] = FALSE;
|
||||
}
|
||||
|
||||
for (scanno = 1; scanno <= cinfo->num_scans; scanptr++, scanno++) {
|
||||
/* Validate component indexes */
|
||||
ncomps = scanptr->comps_in_scan;
|
||||
if (ncomps <= 0 || ncomps > MAX_COMPS_IN_SCAN)
|
||||
ERREXIT2(cinfo, JERR_COMPONENT_COUNT, ncomps, MAX_COMPS_IN_SCAN);
|
||||
for (ci = 0; ci < ncomps; ci++) {
|
||||
thisi = scanptr->component_index[ci];
|
||||
if (thisi < 0 || thisi >= cinfo->num_components)
|
||||
ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno);
|
||||
/* Components must appear in SOF order within each scan */
|
||||
if (ci > 0 && thisi <= scanptr->component_index[ci-1])
|
||||
ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno);
|
||||
}
|
||||
/* Validate progression parameters */
|
||||
Ss = scanptr->Ss;
|
||||
Se = scanptr->Se;
|
||||
Ah = scanptr->Ah;
|
||||
Al = scanptr->Al;
|
||||
if (cinfo->progressive_mode) {
|
||||
#ifdef C_PROGRESSIVE_SUPPORTED
|
||||
if (Ss < 0 || Ss >= DCTSIZE2 || Se < Ss || Se >= DCTSIZE2 ||
|
||||
Ah < 0 || Ah > 13 || Al < 0 || Al > 13)
|
||||
ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
|
||||
if (Ss == 0) {
|
||||
if (Se != 0) /* DC and AC together not OK */
|
||||
ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
|
||||
} else {
|
||||
if (ncomps != 1) /* AC scans must be for only one component */
|
||||
ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
|
||||
}
|
||||
for (ci = 0; ci < ncomps; ci++) {
|
||||
last_bitpos_ptr = & last_bitpos[scanptr->component_index[ci]][0];
|
||||
if (Ss != 0 && last_bitpos_ptr[0] < 0) /* AC without prior DC scan */
|
||||
ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
|
||||
for (coefi = Ss; coefi <= Se; coefi++) {
|
||||
if (last_bitpos_ptr[coefi] < 0) {
|
||||
/* first scan of this coefficient */
|
||||
if (Ah != 0)
|
||||
ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
|
||||
} else {
|
||||
/* not first scan */
|
||||
if (Ah != last_bitpos_ptr[coefi] || Al != Ah-1)
|
||||
ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
|
||||
}
|
||||
last_bitpos_ptr[coefi] = Al;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/* For sequential JPEG, all progression parameters must be these: */
|
||||
if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0)
|
||||
ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno);
|
||||
/* Make sure components are not sent twice */
|
||||
for (ci = 0; ci < ncomps; ci++) {
|
||||
thisi = scanptr->component_index[ci];
|
||||
if (component_sent[thisi])
|
||||
ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno);
|
||||
component_sent[thisi] = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now verify that everything got sent. */
|
||||
if (cinfo->progressive_mode) {
|
||||
#ifdef C_PROGRESSIVE_SUPPORTED
|
||||
/* For progressive mode, we only check that at least some DC data
|
||||
* got sent for each component; the spec does not require that all bits
|
||||
* of all coefficients be transmitted. Would it be wiser to enforce
|
||||
* transmission of all coefficient bits??
|
||||
*/
|
||||
for (ci = 0; ci < cinfo->num_components; ci++) {
|
||||
if (last_bitpos[ci][0] < 0)
|
||||
ERREXIT(cinfo, JERR_MISSING_DATA);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
for (ci = 0; ci < cinfo->num_components; ci++) {
|
||||
if (! component_sent[ci])
|
||||
ERREXIT(cinfo, JERR_MISSING_DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* C_MULTISCAN_FILES_SUPPORTED */
|
||||
|
||||
|
||||
LOCAL void
|
||||
select_scan_parameters (j_compress_ptr cinfo)
|
||||
/* Set up the scan parameters for the current scan */
|
||||
{
|
||||
int ci;
|
||||
|
||||
#ifdef C_MULTISCAN_FILES_SUPPORTED
|
||||
if (cinfo->scan_info != NULL) {
|
||||
/* Prepare for current scan --- the script is already validated */
|
||||
my_master_ptr master = (my_master_ptr) cinfo->master;
|
||||
const jpeg_scan_info * scanptr = cinfo->scan_info + master->scan_number;
|
||||
|
||||
cinfo->comps_in_scan = scanptr->comps_in_scan;
|
||||
for (ci = 0; ci < scanptr->comps_in_scan; ci++) {
|
||||
cinfo->cur_comp_info[ci] =
|
||||
&cinfo->comp_info[scanptr->component_index[ci]];
|
||||
}
|
||||
cinfo->Ss = scanptr->Ss;
|
||||
cinfo->Se = scanptr->Se;
|
||||
cinfo->Ah = scanptr->Ah;
|
||||
cinfo->Al = scanptr->Al;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Prepare for single sequential-JPEG scan containing all components */
|
||||
if (cinfo->num_components > MAX_COMPS_IN_SCAN)
|
||||
ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components,
|
||||
MAX_COMPS_IN_SCAN);
|
||||
cinfo->comps_in_scan = cinfo->num_components;
|
||||
for (ci = 0; ci < cinfo->num_components; ci++) {
|
||||
cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci];
|
||||
}
|
||||
cinfo->Ss = 0;
|
||||
cinfo->Se = DCTSIZE2-1;
|
||||
cinfo->Ah = 0;
|
||||
cinfo->Al = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
per_scan_setup (j_compress_ptr cinfo)
|
||||
/* Do computations that are needed before processing a JPEG scan */
|
||||
/* cinfo->comps_in_scan and cinfo->cur_comp_info[] are already set */
|
||||
{
|
||||
int ci, mcublks, tmp;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
if (cinfo->comps_in_scan == 1) {
|
||||
|
||||
/* Noninterleaved (single-component) scan */
|
||||
compptr = cinfo->cur_comp_info[0];
|
||||
|
||||
/* Overall image size in MCUs */
|
||||
cinfo->MCUs_per_row = compptr->width_in_blocks;
|
||||
cinfo->MCU_rows_in_scan = compptr->height_in_blocks;
|
||||
|
||||
/* For noninterleaved scan, always one block per MCU */
|
||||
compptr->MCU_width = 1;
|
||||
compptr->MCU_height = 1;
|
||||
compptr->MCU_blocks = 1;
|
||||
compptr->MCU_sample_width = DCTSIZE;
|
||||
compptr->last_col_width = 1;
|
||||
/* For noninterleaved scans, it is convenient to define last_row_height
|
||||
* as the number of block rows present in the last iMCU row.
|
||||
*/
|
||||
tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor);
|
||||
if (tmp == 0) tmp = compptr->v_samp_factor;
|
||||
compptr->last_row_height = tmp;
|
||||
|
||||
/* Prepare array describing MCU composition */
|
||||
cinfo->blocks_in_MCU = 1;
|
||||
cinfo->MCU_membership[0] = 0;
|
||||
|
||||
} else {
|
||||
|
||||
/* Interleaved (multi-component) scan */
|
||||
if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN)
|
||||
ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan,
|
||||
MAX_COMPS_IN_SCAN);
|
||||
|
||||
/* Overall image size in MCUs */
|
||||
cinfo->MCUs_per_row = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_width,
|
||||
(long) (cinfo->max_h_samp_factor*DCTSIZE));
|
||||
cinfo->MCU_rows_in_scan = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height,
|
||||
(long) (cinfo->max_v_samp_factor*DCTSIZE));
|
||||
|
||||
cinfo->blocks_in_MCU = 0;
|
||||
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
/* Sampling factors give # of blocks of component in each MCU */
|
||||
compptr->MCU_width = compptr->h_samp_factor;
|
||||
compptr->MCU_height = compptr->v_samp_factor;
|
||||
compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height;
|
||||
compptr->MCU_sample_width = compptr->MCU_width * DCTSIZE;
|
||||
/* Figure number of non-dummy blocks in last MCU column & row */
|
||||
tmp = (int) (compptr->width_in_blocks % compptr->MCU_width);
|
||||
if (tmp == 0) tmp = compptr->MCU_width;
|
||||
compptr->last_col_width = tmp;
|
||||
tmp = (int) (compptr->height_in_blocks % compptr->MCU_height);
|
||||
if (tmp == 0) tmp = compptr->MCU_height;
|
||||
compptr->last_row_height = tmp;
|
||||
/* Prepare array describing MCU composition */
|
||||
mcublks = compptr->MCU_blocks;
|
||||
if (cinfo->blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU)
|
||||
ERREXIT(cinfo, JERR_BAD_MCU_SIZE);
|
||||
while (mcublks-- > 0) {
|
||||
cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Convert restart specified in rows to actual MCU count. */
|
||||
/* Note that count must fit in 16 bits, so we provide limiting. */
|
||||
if (cinfo->restart_in_rows > 0) {
|
||||
long nominal = (long) cinfo->restart_in_rows * (long) cinfo->MCUs_per_row;
|
||||
cinfo->restart_interval = (unsigned int) MIN(nominal, 65535L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Per-pass setup.
|
||||
* This is called at the beginning of each pass. We determine which modules
|
||||
* will be active during this pass and give them appropriate start_pass calls.
|
||||
* We also set is_last_pass to indicate whether any more passes will be
|
||||
* required.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
prepare_for_pass (j_compress_ptr cinfo)
|
||||
{
|
||||
my_master_ptr master = (my_master_ptr) cinfo->master;
|
||||
|
||||
switch (master->pass_type) {
|
||||
case main_pass:
|
||||
/* Initial pass: will collect input data, and do either Huffman
|
||||
* optimization or data output for the first scan.
|
||||
*/
|
||||
select_scan_parameters(cinfo);
|
||||
per_scan_setup(cinfo);
|
||||
if (! cinfo->raw_data_in) {
|
||||
(*cinfo->cconvert->start_pass) (cinfo);
|
||||
(*cinfo->downsample->start_pass) (cinfo);
|
||||
(*cinfo->prep->start_pass) (cinfo, JBUF_PASS_THRU);
|
||||
}
|
||||
(*cinfo->fdct->start_pass) (cinfo);
|
||||
(*cinfo->entropy->start_pass) (cinfo, cinfo->optimize_coding);
|
||||
(*cinfo->coef->start_pass) (cinfo,
|
||||
(master->total_passes > 1 ?
|
||||
JBUF_SAVE_AND_PASS : JBUF_PASS_THRU));
|
||||
(*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU);
|
||||
if (cinfo->optimize_coding) {
|
||||
/* No immediate data output; postpone writing frame/scan headers */
|
||||
master->pub.call_pass_startup = FALSE;
|
||||
} else {
|
||||
/* Will write frame/scan headers at first jpeg_write_scanlines call */
|
||||
master->pub.call_pass_startup = TRUE;
|
||||
}
|
||||
break;
|
||||
#ifdef ENTROPY_OPT_SUPPORTED
|
||||
case huff_opt_pass:
|
||||
/* Do Huffman optimization for a scan after the first one. */
|
||||
select_scan_parameters(cinfo);
|
||||
per_scan_setup(cinfo);
|
||||
if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code) {
|
||||
(*cinfo->entropy->start_pass) (cinfo, TRUE);
|
||||
(*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST);
|
||||
master->pub.call_pass_startup = FALSE;
|
||||
break;
|
||||
}
|
||||
/* Special case: Huffman DC refinement scans need no Huffman table
|
||||
* and therefore we can skip the optimization pass for them.
|
||||
*/
|
||||
master->pass_type = output_pass;
|
||||
master->pass_number++;
|
||||
/*FALLTHROUGH*/
|
||||
#endif
|
||||
case output_pass:
|
||||
/* Do a data-output pass. */
|
||||
/* We need not repeat per-scan setup if prior optimization pass did it. */
|
||||
if (! cinfo->optimize_coding) {
|
||||
select_scan_parameters(cinfo);
|
||||
per_scan_setup(cinfo);
|
||||
}
|
||||
(*cinfo->entropy->start_pass) (cinfo, FALSE);
|
||||
(*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST);
|
||||
/* We emit frame/scan headers now */
|
||||
if (master->scan_number == 0)
|
||||
(*cinfo->marker->write_frame_header) (cinfo);
|
||||
(*cinfo->marker->write_scan_header) (cinfo);
|
||||
master->pub.call_pass_startup = FALSE;
|
||||
break;
|
||||
default:
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
}
|
||||
|
||||
master->pub.is_last_pass = (master->pass_number == master->total_passes-1);
|
||||
|
||||
/* Set up progress monitor's pass info if present */
|
||||
if (cinfo->progress != NULL) {
|
||||
cinfo->progress->completed_passes = master->pass_number;
|
||||
cinfo->progress->total_passes = master->total_passes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Special start-of-pass hook.
|
||||
* This is called by jpeg_write_scanlines if call_pass_startup is TRUE.
|
||||
* In single-pass processing, we need this hook because we don't want to
|
||||
* write frame/scan headers during jpeg_start_compress; we want to let the
|
||||
* application write COM markers etc. between jpeg_start_compress and the
|
||||
* jpeg_write_scanlines loop.
|
||||
* In multi-pass processing, this routine is not used.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
pass_startup (j_compress_ptr cinfo)
|
||||
{
|
||||
cinfo->master->call_pass_startup = FALSE; /* reset flag so call only once */
|
||||
|
||||
(*cinfo->marker->write_frame_header) (cinfo);
|
||||
(*cinfo->marker->write_scan_header) (cinfo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish up at end of pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
finish_pass_master (j_compress_ptr cinfo)
|
||||
{
|
||||
my_master_ptr master = (my_master_ptr) cinfo->master;
|
||||
|
||||
/* The entropy coder always needs an end-of-pass call,
|
||||
* either to analyze statistics or to flush its output buffer.
|
||||
*/
|
||||
(*cinfo->entropy->finish_pass) (cinfo);
|
||||
|
||||
/* Update state for next pass */
|
||||
switch (master->pass_type) {
|
||||
case main_pass:
|
||||
/* next pass is either output of scan 0 (after optimization)
|
||||
* or output of scan 1 (if no optimization).
|
||||
*/
|
||||
master->pass_type = output_pass;
|
||||
if (! cinfo->optimize_coding)
|
||||
master->scan_number++;
|
||||
break;
|
||||
case huff_opt_pass:
|
||||
/* next pass is always output of current scan */
|
||||
master->pass_type = output_pass;
|
||||
break;
|
||||
case output_pass:
|
||||
/* next pass is either optimization or output of next scan */
|
||||
if (cinfo->optimize_coding)
|
||||
master->pass_type = huff_opt_pass;
|
||||
master->scan_number++;
|
||||
break;
|
||||
}
|
||||
|
||||
master->pass_number++;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize master compression control.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only)
|
||||
{
|
||||
my_master_ptr master;
|
||||
|
||||
master = (my_master_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_comp_master));
|
||||
cinfo->master = (struct jpeg_comp_master *) master;
|
||||
master->pub.prepare_for_pass = prepare_for_pass;
|
||||
master->pub.pass_startup = pass_startup;
|
||||
master->pub.finish_pass = finish_pass_master;
|
||||
master->pub.is_last_pass = FALSE;
|
||||
|
||||
/* Validate parameters, determine derived values */
|
||||
initial_setup(cinfo);
|
||||
|
||||
if (cinfo->scan_info != NULL) {
|
||||
#ifdef C_MULTISCAN_FILES_SUPPORTED
|
||||
validate_script(cinfo);
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
} else {
|
||||
cinfo->progressive_mode = FALSE;
|
||||
cinfo->num_scans = 1;
|
||||
}
|
||||
|
||||
if (cinfo->progressive_mode) /* TEMPORARY HACK ??? */
|
||||
cinfo->optimize_coding = TRUE; /* assume default tables no good for progressive mode */
|
||||
|
||||
/* Initialize my private state */
|
||||
if (transcode_only) {
|
||||
/* no main pass in transcoding */
|
||||
if (cinfo->optimize_coding)
|
||||
master->pass_type = huff_opt_pass;
|
||||
else
|
||||
master->pass_type = output_pass;
|
||||
} else {
|
||||
/* for normal compression, first pass is always this type: */
|
||||
master->pass_type = main_pass;
|
||||
}
|
||||
master->scan_number = 0;
|
||||
master->pass_number = 0;
|
||||
if (cinfo->optimize_coding)
|
||||
master->total_passes = cinfo->num_scans * 2;
|
||||
else
|
||||
master->total_passes = cinfo->num_scans;
|
||||
}
|
||||
94
neo/renderer/jpeg-6/jcomapi.c
Normal file
94
neo/renderer/jpeg-6/jcomapi.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* jcomapi.c
|
||||
*
|
||||
* Copyright (C) 1994, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains application interface routines that are used for both
|
||||
* compression and decompression.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/*
|
||||
* Abort processing of a JPEG compression or decompression operation,
|
||||
* but don't destroy the object itself.
|
||||
*
|
||||
* For this, we merely clean up all the nonpermanent memory pools.
|
||||
* Note that temp files (virtual arrays) are not allowed to belong to
|
||||
* the permanent pool, so we will be able to close all temp files here.
|
||||
* Closing a data source or destination, if necessary, is the application's
|
||||
* responsibility.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_abort (j_common_ptr cinfo)
|
||||
{
|
||||
int pool;
|
||||
|
||||
/* Releasing pools in reverse order might help avoid fragmentation
|
||||
* with some (brain-damaged) malloc libraries.
|
||||
*/
|
||||
for (pool = JPOOL_NUMPOOLS-1; pool > JPOOL_PERMANENT; pool--) {
|
||||
(*cinfo->mem->free_pool) (cinfo, pool);
|
||||
}
|
||||
|
||||
/* Reset overall state for possible reuse of object */
|
||||
cinfo->global_state = (cinfo->is_decompressor ? DSTATE_START : CSTATE_START);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Destruction of a JPEG object.
|
||||
*
|
||||
* Everything gets deallocated except the master jpeg_compress_struct itself
|
||||
* and the error manager struct. Both of these are supplied by the application
|
||||
* and must be freed, if necessary, by the application. (Often they are on
|
||||
* the stack and so don't need to be freed anyway.)
|
||||
* Closing a data source or destination, if necessary, is the application's
|
||||
* responsibility.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_destroy (j_common_ptr cinfo)
|
||||
{
|
||||
/* We need only tell the memory manager to release everything. */
|
||||
/* NB: mem pointer is NULL if memory mgr failed to initialize. */
|
||||
if (cinfo->mem != NULL)
|
||||
(*cinfo->mem->self_destruct) (cinfo);
|
||||
cinfo->mem = NULL; /* be safe if jpeg_destroy is called twice */
|
||||
cinfo->global_state = 0; /* mark it destroyed */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convenience routines for allocating quantization and Huffman tables.
|
||||
* (Would jutils.c be a more reasonable place to put these?)
|
||||
*/
|
||||
|
||||
GLOBAL JQUANT_TBL *
|
||||
jpeg_alloc_quant_table (j_common_ptr cinfo)
|
||||
{
|
||||
JQUANT_TBL *tbl;
|
||||
|
||||
tbl = (JQUANT_TBL *)
|
||||
(*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL));
|
||||
tbl->sent_table = FALSE; /* make sure this is false in any new table */
|
||||
return tbl;
|
||||
}
|
||||
|
||||
|
||||
GLOBAL JHUFF_TBL *
|
||||
jpeg_alloc_huff_table (j_common_ptr cinfo)
|
||||
{
|
||||
JHUFF_TBL *tbl;
|
||||
|
||||
tbl = (JHUFF_TBL *)
|
||||
(*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL));
|
||||
tbl->sent_table = FALSE; /* make sure this is false in any new table */
|
||||
return tbl;
|
||||
}
|
||||
41
neo/renderer/jpeg-6/jconfig.h
Normal file
41
neo/renderer/jpeg-6/jconfig.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* jconfig.wat --- jconfig.h for Watcom C/C++ on MS-DOS or OS/2. */
|
||||
/* see jconfig.doc for explanations */
|
||||
|
||||
#define HAVE_PROTOTYPES
|
||||
#define HAVE_UNSIGNED_CHAR
|
||||
#define HAVE_UNSIGNED_SHORT
|
||||
/* #define void char */
|
||||
/* #define const */
|
||||
#define CHAR_IS_UNSIGNED
|
||||
#define HAVE_STDDEF_H
|
||||
#define HAVE_STDLIB_H
|
||||
#undef NEED_BSD_STRINGS
|
||||
#undef NEED_SYS_TYPES_H
|
||||
#undef NEED_FAR_POINTERS /* Watcom uses flat 32-bit addressing */
|
||||
#undef NEED_SHORT_EXTERNAL_NAMES
|
||||
#undef INCOMPLETE_TYPES_BROKEN
|
||||
|
||||
#define JDCT_DEFAULT JDCT_FLOAT
|
||||
#define JDCT_FASTEST JDCT_FLOAT
|
||||
|
||||
#ifdef JPEG_INTERNALS
|
||||
|
||||
#undef RIGHT_SHIFT_IS_UNSIGNED
|
||||
|
||||
#endif /* JPEG_INTERNALS */
|
||||
|
||||
#ifdef JPEG_CJPEG_DJPEG
|
||||
|
||||
#define BMP_SUPPORTED /* BMP image file format */
|
||||
#define GIF_SUPPORTED /* GIF image file format */
|
||||
#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */
|
||||
#undef RLE_SUPPORTED /* Utah RLE image file format */
|
||||
#define TARGA_SUPPORTED /* Targa image file format */
|
||||
|
||||
#undef TWO_FILE_COMMANDLINE /* optional */
|
||||
#define USE_SETMODE /* Needed to make one-file style work in Watcom */
|
||||
#undef NEED_SIGNAL_CATCHER /* Define this if you use jmemname.c */
|
||||
#undef DONT_USE_B_MODE
|
||||
#undef PROGRESS_REPORT /* optional */
|
||||
|
||||
#endif /* JPEG_CJPEG_DJPEG */
|
||||
575
neo/renderer/jpeg-6/jcparam.c
Normal file
575
neo/renderer/jpeg-6/jcparam.c
Normal file
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
* jcparam.c
|
||||
*
|
||||
* Copyright (C) 1991-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains optional default-setting code for the JPEG compressor.
|
||||
* Applications do not have to use this file, but those that don't use it
|
||||
* must know a lot more about the innards of the JPEG code.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/*
|
||||
* Quantization table setup routines
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_add_quant_table (j_compress_ptr cinfo, int which_tbl,
|
||||
const unsigned int *basic_table,
|
||||
int scale_factor, boolean force_baseline)
|
||||
/* Define a quantization table equal to the basic_table times
|
||||
* a scale factor (given as a percentage).
|
||||
* If force_baseline is TRUE, the computed quantization table entries
|
||||
* are limited to 1..255 for JPEG baseline compatibility.
|
||||
*/
|
||||
{
|
||||
JQUANT_TBL ** qtblptr = & cinfo->quant_tbl_ptrs[which_tbl];
|
||||
int i;
|
||||
long temp;
|
||||
|
||||
/* Safety check to ensure start_compress not called yet. */
|
||||
if (cinfo->global_state != CSTATE_START)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
|
||||
if (*qtblptr == NULL)
|
||||
*qtblptr = jpeg_alloc_quant_table((j_common_ptr) cinfo);
|
||||
|
||||
for (i = 0; i < DCTSIZE2; i++) {
|
||||
temp = ((long) basic_table[i] * scale_factor + 50L) / 100L;
|
||||
/* limit the values to the valid range */
|
||||
if (temp <= 0L) temp = 1L;
|
||||
if (temp > 32767L) temp = 32767L; /* max quantizer needed for 12 bits */
|
||||
if (force_baseline && temp > 255L)
|
||||
temp = 255L; /* limit to baseline range if requested */
|
||||
(*qtblptr)->quantval[i] = (UINT16) temp;
|
||||
}
|
||||
|
||||
/* Initialize sent_table FALSE so table will be written to JPEG file. */
|
||||
(*qtblptr)->sent_table = FALSE;
|
||||
}
|
||||
|
||||
|
||||
GLOBAL void
|
||||
jpeg_set_linear_quality (j_compress_ptr cinfo, int scale_factor,
|
||||
boolean force_baseline)
|
||||
/* Set or change the 'quality' (quantization) setting, using default tables
|
||||
* and a straight percentage-scaling quality scale. In most cases it's better
|
||||
* to use jpeg_set_quality (below); this entry point is provided for
|
||||
* applications that insist on a linear percentage scaling.
|
||||
*/
|
||||
{
|
||||
/* This is the sample quantization table given in the JPEG spec section K.1,
|
||||
* but expressed in zigzag order (as are all of our quant. tables).
|
||||
* The spec says that the values given produce "good" quality, and
|
||||
* when divided by 2, "very good" quality.
|
||||
*/
|
||||
static const unsigned int std_luminance_quant_tbl[DCTSIZE2] = {
|
||||
16, 11, 12, 14, 12, 10, 16, 14,
|
||||
13, 14, 18, 17, 16, 19, 24, 40,
|
||||
26, 24, 22, 22, 24, 49, 35, 37,
|
||||
29, 40, 58, 51, 61, 60, 57, 51,
|
||||
56, 55, 64, 72, 92, 78, 64, 68,
|
||||
87, 69, 55, 56, 80, 109, 81, 87,
|
||||
95, 98, 103, 104, 103, 62, 77, 113,
|
||||
121, 112, 100, 120, 92, 101, 103, 99
|
||||
};
|
||||
static const unsigned int std_chrominance_quant_tbl[DCTSIZE2] = {
|
||||
17, 18, 18, 24, 21, 24, 47, 26,
|
||||
26, 47, 99, 66, 56, 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, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99
|
||||
};
|
||||
|
||||
/* Set up two quantization tables using the specified scaling */
|
||||
jpeg_add_quant_table(cinfo, 0, std_luminance_quant_tbl,
|
||||
scale_factor, force_baseline);
|
||||
jpeg_add_quant_table(cinfo, 1, std_chrominance_quant_tbl,
|
||||
scale_factor, force_baseline);
|
||||
}
|
||||
|
||||
|
||||
GLOBAL int
|
||||
jpeg_quality_scaling (int quality)
|
||||
/* Convert a user-specified quality rating to a percentage scaling factor
|
||||
* for an underlying quantization table, using our recommended scaling curve.
|
||||
* The input 'quality' factor should be 0 (terrible) to 100 (very good).
|
||||
*/
|
||||
{
|
||||
/* Safety limit on quality factor. Convert 0 to 1 to avoid zero divide. */
|
||||
if (quality <= 0) quality = 1;
|
||||
if (quality > 100) quality = 100;
|
||||
|
||||
/* The basic table is used as-is (scaling 100) for a quality of 50.
|
||||
* Qualities 50..100 are converted to scaling percentage 200 - 2*Q;
|
||||
* note that at Q=100 the scaling is 0, which will cause j_add_quant_table
|
||||
* to make all the table entries 1 (hence, no quantization loss).
|
||||
* Qualities 1..50 are converted to scaling percentage 5000/Q.
|
||||
*/
|
||||
if (quality < 50)
|
||||
quality = 5000 / quality;
|
||||
else
|
||||
quality = 200 - quality*2;
|
||||
|
||||
return quality;
|
||||
}
|
||||
|
||||
|
||||
GLOBAL void
|
||||
jpeg_set_quality (j_compress_ptr cinfo, int quality, boolean force_baseline)
|
||||
/* Set or change the 'quality' (quantization) setting, using default tables.
|
||||
* This is the standard quality-adjusting entry point for typical user
|
||||
* interfaces; only those who want detailed control over quantization tables
|
||||
* would use the preceding three routines directly.
|
||||
*/
|
||||
{
|
||||
/* Convert user 0-100 rating to percentage scaling */
|
||||
quality = jpeg_quality_scaling(quality);
|
||||
|
||||
/* Set up standard quality tables */
|
||||
jpeg_set_linear_quality(cinfo, quality, force_baseline);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Huffman table setup routines
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
add_huff_table (j_compress_ptr cinfo,
|
||||
JHUFF_TBL **htblptr, const UINT8 *bits, const UINT8 *val)
|
||||
/* Define a Huffman table */
|
||||
{
|
||||
if (*htblptr == NULL)
|
||||
*htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo);
|
||||
|
||||
MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits));
|
||||
MEMCOPY((*htblptr)->huffval, val, SIZEOF((*htblptr)->huffval));
|
||||
|
||||
/* Initialize sent_table FALSE so table will be written to JPEG file. */
|
||||
(*htblptr)->sent_table = FALSE;
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
std_huff_tables (j_compress_ptr cinfo)
|
||||
/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */
|
||||
/* IMPORTANT: these are only valid for 8-bit data precision! */
|
||||
{
|
||||
static const UINT8 bits_dc_luminance[17] =
|
||||
{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
|
||||
static const UINT8 val_dc_luminance[] =
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
|
||||
|
||||
static const UINT8 bits_dc_chrominance[17] =
|
||||
{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 };
|
||||
static const UINT8 val_dc_chrominance[] =
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
|
||||
|
||||
static const UINT8 bits_ac_luminance[17] =
|
||||
{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d };
|
||||
static const UINT8 val_ac_luminance[] =
|
||||
{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa };
|
||||
|
||||
static const UINT8 bits_ac_chrominance[17] =
|
||||
{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 };
|
||||
static const UINT8 val_ac_chrominance[] =
|
||||
{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa };
|
||||
|
||||
add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[0],
|
||||
bits_dc_luminance, val_dc_luminance);
|
||||
add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[0],
|
||||
bits_ac_luminance, val_ac_luminance);
|
||||
add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[1],
|
||||
bits_dc_chrominance, val_dc_chrominance);
|
||||
add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[1],
|
||||
bits_ac_chrominance, val_ac_chrominance);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Default parameter setup for compression.
|
||||
*
|
||||
* Applications that don't choose to use this routine must do their
|
||||
* own setup of all these parameters. Alternately, you can call this
|
||||
* to establish defaults and then alter parameters selectively. This
|
||||
* is the recommended approach since, if we add any new parameters,
|
||||
* your code will still work (they'll be set to reasonable defaults).
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_set_defaults (j_compress_ptr cinfo)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Safety check to ensure start_compress not called yet. */
|
||||
if (cinfo->global_state != CSTATE_START)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
|
||||
/* Allocate comp_info array large enough for maximum component count.
|
||||
* Array is made permanent in case application wants to compress
|
||||
* multiple images at same param settings.
|
||||
*/
|
||||
if (cinfo->comp_info == NULL)
|
||||
cinfo->comp_info = (jpeg_component_info *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
|
||||
MAX_COMPONENTS * SIZEOF(jpeg_component_info));
|
||||
|
||||
/* Initialize everything not dependent on the color space */
|
||||
|
||||
cinfo->data_precision = BITS_IN_JSAMPLE;
|
||||
/* Set up two quantization tables using default quality of 75 */
|
||||
jpeg_set_quality(cinfo, 75, TRUE);
|
||||
/* Set up two Huffman tables */
|
||||
std_huff_tables(cinfo);
|
||||
|
||||
/* Initialize default arithmetic coding conditioning */
|
||||
for (i = 0; i < NUM_ARITH_TBLS; i++) {
|
||||
cinfo->arith_dc_L[i] = 0;
|
||||
cinfo->arith_dc_U[i] = 1;
|
||||
cinfo->arith_ac_K[i] = 5;
|
||||
}
|
||||
|
||||
/* Default is no multiple-scan output */
|
||||
cinfo->scan_info = NULL;
|
||||
cinfo->num_scans = 0;
|
||||
|
||||
/* Expect normal source image, not raw downsampled data */
|
||||
cinfo->raw_data_in = FALSE;
|
||||
|
||||
/* Use Huffman coding, not arithmetic coding, by default */
|
||||
cinfo->arith_code = FALSE;
|
||||
|
||||
/* By default, don't do extra passes to optimize entropy coding */
|
||||
cinfo->optimize_coding = FALSE;
|
||||
/* The standard Huffman tables are only valid for 8-bit data precision.
|
||||
* If the precision is higher, force optimization on so that usable
|
||||
* tables will be computed. This test can be removed if default tables
|
||||
* are supplied that are valid for the desired precision.
|
||||
*/
|
||||
if (cinfo->data_precision > 8)
|
||||
cinfo->optimize_coding = TRUE;
|
||||
|
||||
/* By default, use the simpler non-cosited sampling alignment */
|
||||
cinfo->CCIR601_sampling = FALSE;
|
||||
|
||||
/* No input smoothing */
|
||||
cinfo->smoothing_factor = 0;
|
||||
|
||||
/* DCT algorithm preference */
|
||||
cinfo->dct_method = JDCT_DEFAULT;
|
||||
|
||||
/* No restart markers */
|
||||
cinfo->restart_interval = 0;
|
||||
cinfo->restart_in_rows = 0;
|
||||
|
||||
/* Fill in default JFIF marker parameters. Note that whether the marker
|
||||
* will actually be written is determined by jpeg_set_colorspace.
|
||||
*/
|
||||
cinfo->density_unit = 0; /* Pixel size is unknown by default */
|
||||
cinfo->X_density = 1; /* Pixel aspect ratio is square by default */
|
||||
cinfo->Y_density = 1;
|
||||
|
||||
/* Choose JPEG colorspace based on input space, set defaults accordingly */
|
||||
|
||||
jpeg_default_colorspace(cinfo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Select an appropriate JPEG colorspace for in_color_space.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_default_colorspace (j_compress_ptr cinfo)
|
||||
{
|
||||
switch (cinfo->in_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
|
||||
break;
|
||||
case JCS_RGB:
|
||||
jpeg_set_colorspace(cinfo, JCS_YCbCr);
|
||||
break;
|
||||
case JCS_YCbCr:
|
||||
jpeg_set_colorspace(cinfo, JCS_YCbCr);
|
||||
break;
|
||||
case JCS_CMYK:
|
||||
jpeg_set_colorspace(cinfo, JCS_CMYK); /* By default, no translation */
|
||||
break;
|
||||
case JCS_YCCK:
|
||||
jpeg_set_colorspace(cinfo, JCS_YCCK);
|
||||
break;
|
||||
case JCS_UNKNOWN:
|
||||
jpeg_set_colorspace(cinfo, JCS_UNKNOWN);
|
||||
break;
|
||||
default:
|
||||
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the JPEG colorspace, and choose colorspace-dependent default values.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace)
|
||||
{
|
||||
jpeg_component_info * compptr;
|
||||
int ci;
|
||||
|
||||
#define SET_COMP(index,id,hsamp,vsamp,quant,dctbl,actbl) \
|
||||
(compptr = &cinfo->comp_info[index], \
|
||||
compptr->component_id = (id), \
|
||||
compptr->h_samp_factor = (hsamp), \
|
||||
compptr->v_samp_factor = (vsamp), \
|
||||
compptr->quant_tbl_no = (quant), \
|
||||
compptr->dc_tbl_no = (dctbl), \
|
||||
compptr->ac_tbl_no = (actbl) )
|
||||
|
||||
/* Safety check to ensure start_compress not called yet. */
|
||||
if (cinfo->global_state != CSTATE_START)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
|
||||
/* For all colorspaces, we use Q and Huff tables 0 for luminance components,
|
||||
* tables 1 for chrominance components.
|
||||
*/
|
||||
|
||||
cinfo->jpeg_color_space = colorspace;
|
||||
|
||||
cinfo->write_JFIF_header = FALSE; /* No marker for non-JFIF colorspaces */
|
||||
cinfo->write_Adobe_marker = FALSE; /* write no Adobe marker by default */
|
||||
|
||||
switch (colorspace) {
|
||||
case JCS_GRAYSCALE:
|
||||
cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */
|
||||
cinfo->num_components = 1;
|
||||
/* JFIF specifies component ID 1 */
|
||||
SET_COMP(0, 1, 1,1, 0, 0,0);
|
||||
break;
|
||||
case JCS_RGB:
|
||||
cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag RGB */
|
||||
cinfo->num_components = 3;
|
||||
SET_COMP(0, 0x52 /* 'R' */, 1,1, 0, 0,0);
|
||||
SET_COMP(1, 0x47 /* 'G' */, 1,1, 0, 0,0);
|
||||
SET_COMP(2, 0x42 /* 'B' */, 1,1, 0, 0,0);
|
||||
break;
|
||||
case JCS_YCbCr:
|
||||
cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */
|
||||
cinfo->num_components = 3;
|
||||
/* JFIF specifies component IDs 1,2,3 */
|
||||
/* We default to 2x2 subsamples of chrominance */
|
||||
SET_COMP(0, 1, 2,2, 0, 0,0);
|
||||
SET_COMP(1, 2, 1,1, 1, 1,1);
|
||||
SET_COMP(2, 3, 1,1, 1, 1,1);
|
||||
break;
|
||||
case JCS_CMYK:
|
||||
cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag CMYK */
|
||||
cinfo->num_components = 4;
|
||||
SET_COMP(0, 0x43 /* 'C' */, 1,1, 0, 0,0);
|
||||
SET_COMP(1, 0x4D /* 'M' */, 1,1, 0, 0,0);
|
||||
SET_COMP(2, 0x59 /* 'Y' */, 1,1, 0, 0,0);
|
||||
SET_COMP(3, 0x4B /* 'K' */, 1,1, 0, 0,0);
|
||||
break;
|
||||
case JCS_YCCK:
|
||||
cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag YCCK */
|
||||
cinfo->num_components = 4;
|
||||
SET_COMP(0, 1, 2,2, 0, 0,0);
|
||||
SET_COMP(1, 2, 1,1, 1, 1,1);
|
||||
SET_COMP(2, 3, 1,1, 1, 1,1);
|
||||
SET_COMP(3, 4, 2,2, 0, 0,0);
|
||||
break;
|
||||
case JCS_UNKNOWN:
|
||||
cinfo->num_components = cinfo->input_components;
|
||||
if (cinfo->num_components < 1 || cinfo->num_components > MAX_COMPONENTS)
|
||||
ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components,
|
||||
MAX_COMPONENTS);
|
||||
for (ci = 0; ci < cinfo->num_components; ci++) {
|
||||
SET_COMP(ci, ci, 1,1, 0, 0,0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef C_PROGRESSIVE_SUPPORTED
|
||||
|
||||
LOCAL jpeg_scan_info *
|
||||
fill_a_scan (jpeg_scan_info * scanptr, int ci,
|
||||
int Ss, int Se, int Ah, int Al)
|
||||
/* Support routine: generate one scan for specified component */
|
||||
{
|
||||
scanptr->comps_in_scan = 1;
|
||||
scanptr->component_index[0] = ci;
|
||||
scanptr->Ss = Ss;
|
||||
scanptr->Se = Se;
|
||||
scanptr->Ah = Ah;
|
||||
scanptr->Al = Al;
|
||||
scanptr++;
|
||||
return scanptr;
|
||||
}
|
||||
|
||||
LOCAL jpeg_scan_info *
|
||||
fill_scans (jpeg_scan_info * scanptr, int ncomps,
|
||||
int Ss, int Se, int Ah, int Al)
|
||||
/* Support routine: generate one scan for each component */
|
||||
{
|
||||
int ci;
|
||||
|
||||
for (ci = 0; ci < ncomps; ci++) {
|
||||
scanptr->comps_in_scan = 1;
|
||||
scanptr->component_index[0] = ci;
|
||||
scanptr->Ss = Ss;
|
||||
scanptr->Se = Se;
|
||||
scanptr->Ah = Ah;
|
||||
scanptr->Al = Al;
|
||||
scanptr++;
|
||||
}
|
||||
return scanptr;
|
||||
}
|
||||
|
||||
LOCAL jpeg_scan_info *
|
||||
fill_dc_scans (jpeg_scan_info * scanptr, int ncomps, int Ah, int Al)
|
||||
/* Support routine: generate interleaved DC scan if possible, else N scans */
|
||||
{
|
||||
int ci;
|
||||
|
||||
if (ncomps <= MAX_COMPS_IN_SCAN) {
|
||||
/* Single interleaved DC scan */
|
||||
scanptr->comps_in_scan = ncomps;
|
||||
for (ci = 0; ci < ncomps; ci++)
|
||||
scanptr->component_index[ci] = ci;
|
||||
scanptr->Ss = scanptr->Se = 0;
|
||||
scanptr->Ah = Ah;
|
||||
scanptr->Al = Al;
|
||||
scanptr++;
|
||||
} else {
|
||||
/* Noninterleaved DC scan for each component */
|
||||
scanptr = fill_scans(scanptr, ncomps, 0, 0, Ah, Al);
|
||||
}
|
||||
return scanptr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a recommended progressive-JPEG script.
|
||||
* cinfo->num_components and cinfo->jpeg_color_space must be correct.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_simple_progression (j_compress_ptr cinfo)
|
||||
{
|
||||
int ncomps = cinfo->num_components;
|
||||
int nscans;
|
||||
jpeg_scan_info * scanptr;
|
||||
|
||||
/* Safety check to ensure start_compress not called yet. */
|
||||
if (cinfo->global_state != CSTATE_START)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
|
||||
/* Figure space needed for script. Calculation must match code below! */
|
||||
if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) {
|
||||
/* Custom script for YCbCr color images. */
|
||||
nscans = 10;
|
||||
} else {
|
||||
/* All-purpose script for other color spaces. */
|
||||
if (ncomps > MAX_COMPS_IN_SCAN)
|
||||
nscans = 6 * ncomps; /* 2 DC + 4 AC scans per component */
|
||||
else
|
||||
nscans = 2 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */
|
||||
}
|
||||
|
||||
/* Allocate space for script. */
|
||||
/* We use permanent pool just in case application re-uses script. */
|
||||
scanptr = (jpeg_scan_info *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
|
||||
nscans * SIZEOF(jpeg_scan_info));
|
||||
cinfo->scan_info = scanptr;
|
||||
cinfo->num_scans = nscans;
|
||||
|
||||
if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) {
|
||||
/* Custom script for YCbCr color images. */
|
||||
/* Initial DC scan */
|
||||
scanptr = fill_dc_scans(scanptr, ncomps, 0, 1);
|
||||
/* Initial AC scan: get some luma data out in a hurry */
|
||||
scanptr = fill_a_scan(scanptr, 0, 1, 5, 0, 2);
|
||||
/* Chroma data is too small to be worth expending many scans on */
|
||||
scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 1);
|
||||
scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 1);
|
||||
/* Complete spectral selection for luma AC */
|
||||
scanptr = fill_a_scan(scanptr, 0, 6, 63, 0, 2);
|
||||
/* Refine next bit of luma AC */
|
||||
scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1);
|
||||
/* Finish DC successive approximation */
|
||||
scanptr = fill_dc_scans(scanptr, ncomps, 1, 0);
|
||||
/* Finish AC successive approximation */
|
||||
scanptr = fill_a_scan(scanptr, 2, 1, 63, 1, 0);
|
||||
scanptr = fill_a_scan(scanptr, 1, 1, 63, 1, 0);
|
||||
/* Luma bottom bit comes last since it's usually largest scan */
|
||||
scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0);
|
||||
} else {
|
||||
/* All-purpose script for other color spaces. */
|
||||
/* Successive approximation first pass */
|
||||
scanptr = fill_dc_scans(scanptr, ncomps, 0, 1);
|
||||
scanptr = fill_scans(scanptr, ncomps, 1, 5, 0, 2);
|
||||
scanptr = fill_scans(scanptr, ncomps, 6, 63, 0, 2);
|
||||
/* Successive approximation second pass */
|
||||
scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1);
|
||||
/* Successive approximation final pass */
|
||||
scanptr = fill_dc_scans(scanptr, ncomps, 1, 0);
|
||||
scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* C_PROGRESSIVE_SUPPORTED */
|
||||
829
neo/renderer/jpeg-6/jcphuff.c
Normal file
829
neo/renderer/jpeg-6/jcphuff.c
Normal file
@@ -0,0 +1,829 @@
|
||||
/*
|
||||
* jcphuff.c
|
||||
*
|
||||
* Copyright (C) 1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains Huffman entropy encoding routines for progressive JPEG.
|
||||
*
|
||||
* We do not support output suspension in this module, since the library
|
||||
* currently does not allow multiple-scan files to be written with output
|
||||
* suspension.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jchuff.h" /* Declarations shared with jchuff.c */
|
||||
|
||||
#ifdef C_PROGRESSIVE_SUPPORTED
|
||||
|
||||
/* Expanded entropy encoder object for progressive Huffman encoding. */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_entropy_encoder pub; /* public fields */
|
||||
|
||||
/* Mode flag: TRUE for optimization, FALSE for actual data output */
|
||||
boolean gather_statistics;
|
||||
|
||||
/* Bit-level coding status.
|
||||
* next_output_byte/free_in_buffer are local copies of cinfo->dest fields.
|
||||
*/
|
||||
JOCTET * next_output_byte; /* => next byte to write in buffer */
|
||||
size_t free_in_buffer; /* # of byte spaces remaining in buffer */
|
||||
INT32 put_buffer; /* current bit-accumulation buffer */
|
||||
int put_bits; /* # of bits now in it */
|
||||
j_compress_ptr cinfo; /* link to cinfo (needed for dump_buffer) */
|
||||
|
||||
/* Coding status for DC components */
|
||||
int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */
|
||||
|
||||
/* Coding status for AC components */
|
||||
int ac_tbl_no; /* the table number of the single component */
|
||||
unsigned int EOBRUN; /* run length of EOBs */
|
||||
unsigned int BE; /* # of buffered correction bits before MCU */
|
||||
char * bit_buffer; /* buffer for correction bits (1 per char) */
|
||||
/* packing correction bits tightly would save some space but cost time... */
|
||||
|
||||
unsigned int restarts_to_go; /* MCUs left in this restart interval */
|
||||
int next_restart_num; /* next restart number to write (0-7) */
|
||||
|
||||
/* Pointers to derived tables (these workspaces have image lifespan).
|
||||
* Since any one scan codes only DC or only AC, we only need one set
|
||||
* of tables, not one for DC and one for AC.
|
||||
*/
|
||||
c_derived_tbl * derived_tbls[NUM_HUFF_TBLS];
|
||||
|
||||
/* Statistics tables for optimization; again, one set is enough */
|
||||
long * count_ptrs[NUM_HUFF_TBLS];
|
||||
} phuff_entropy_encoder;
|
||||
|
||||
typedef phuff_entropy_encoder * phuff_entropy_ptr;
|
||||
|
||||
/* MAX_CORR_BITS is the number of bits the AC refinement correction-bit
|
||||
* buffer can hold. Larger sizes may slightly improve compression, but
|
||||
* 1000 is already well into the realm of overkill.
|
||||
* The minimum safe size is 64 bits.
|
||||
*/
|
||||
|
||||
#define MAX_CORR_BITS 1000 /* Max # of correction bits I can buffer */
|
||||
|
||||
/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than INT32.
|
||||
* We assume that int right shift is unsigned if INT32 right shift is,
|
||||
* which should be safe.
|
||||
*/
|
||||
|
||||
#ifdef RIGHT_SHIFT_IS_UNSIGNED
|
||||
#define ISHIFT_TEMPS int ishift_temp;
|
||||
#define IRIGHT_SHIFT(x,shft) \
|
||||
((ishift_temp = (x)) < 0 ? \
|
||||
(ishift_temp >> (shft)) | ((~0) << (16-(shft))) : \
|
||||
(ishift_temp >> (shft)))
|
||||
#else
|
||||
#define ISHIFT_TEMPS
|
||||
#define IRIGHT_SHIFT(x,shft) ((x) >> (shft))
|
||||
#endif
|
||||
|
||||
/* Forward declarations */
|
||||
METHODDEF boolean encode_mcu_DC_first JPP((j_compress_ptr cinfo,
|
||||
JBLOCKROW *MCU_data));
|
||||
METHODDEF boolean encode_mcu_AC_first JPP((j_compress_ptr cinfo,
|
||||
JBLOCKROW *MCU_data));
|
||||
METHODDEF boolean encode_mcu_DC_refine JPP((j_compress_ptr cinfo,
|
||||
JBLOCKROW *MCU_data));
|
||||
METHODDEF boolean encode_mcu_AC_refine JPP((j_compress_ptr cinfo,
|
||||
JBLOCKROW *MCU_data));
|
||||
METHODDEF void finish_pass_phuff JPP((j_compress_ptr cinfo));
|
||||
METHODDEF void finish_pass_gather_phuff JPP((j_compress_ptr cinfo));
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a Huffman-compressed scan using progressive JPEG.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
boolean is_DC_band;
|
||||
int ci, tbl;
|
||||
jpeg_component_info * compptr;
|
||||
|
||||
entropy->cinfo = cinfo;
|
||||
entropy->gather_statistics = gather_statistics;
|
||||
|
||||
is_DC_band = (cinfo->Ss == 0);
|
||||
|
||||
/* We assume jcmaster.c already validated the scan parameters. */
|
||||
|
||||
/* Select execution routines */
|
||||
if (cinfo->Ah == 0) {
|
||||
if (is_DC_band)
|
||||
entropy->pub.encode_mcu = encode_mcu_DC_first;
|
||||
else
|
||||
entropy->pub.encode_mcu = encode_mcu_AC_first;
|
||||
} else {
|
||||
if (is_DC_band)
|
||||
entropy->pub.encode_mcu = encode_mcu_DC_refine;
|
||||
else {
|
||||
entropy->pub.encode_mcu = encode_mcu_AC_refine;
|
||||
/* AC refinement needs a correction bit buffer */
|
||||
if (entropy->bit_buffer == NULL)
|
||||
entropy->bit_buffer = (char *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
MAX_CORR_BITS * SIZEOF(char));
|
||||
}
|
||||
}
|
||||
if (gather_statistics)
|
||||
entropy->pub.finish_pass = finish_pass_gather_phuff;
|
||||
else
|
||||
entropy->pub.finish_pass = finish_pass_phuff;
|
||||
|
||||
/* Only DC coefficients may be interleaved, so cinfo->comps_in_scan = 1
|
||||
* for AC coefficients.
|
||||
*/
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
/* Initialize DC predictions to 0 */
|
||||
entropy->last_dc_val[ci] = 0;
|
||||
/* Make sure requested tables are present */
|
||||
/* (In gather mode, tables need not be allocated yet) */
|
||||
if (is_DC_band) {
|
||||
if (cinfo->Ah != 0) /* DC refinement needs no table */
|
||||
continue;
|
||||
tbl = compptr->dc_tbl_no;
|
||||
if (tbl < 0 || tbl >= NUM_HUFF_TBLS ||
|
||||
(cinfo->dc_huff_tbl_ptrs[tbl] == NULL && !gather_statistics))
|
||||
ERREXIT1(cinfo,JERR_NO_HUFF_TABLE, tbl);
|
||||
} else {
|
||||
entropy->ac_tbl_no = tbl = compptr->ac_tbl_no;
|
||||
if (tbl < 0 || tbl >= NUM_HUFF_TBLS ||
|
||||
(cinfo->ac_huff_tbl_ptrs[tbl] == NULL && !gather_statistics))
|
||||
ERREXIT1(cinfo,JERR_NO_HUFF_TABLE, tbl);
|
||||
}
|
||||
if (gather_statistics) {
|
||||
/* Allocate and zero the statistics tables */
|
||||
/* Note that jpeg_gen_optimal_table expects 257 entries in each table! */
|
||||
if (entropy->count_ptrs[tbl] == NULL)
|
||||
entropy->count_ptrs[tbl] = (long *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
257 * SIZEOF(long));
|
||||
MEMZERO(entropy->count_ptrs[tbl], 257 * SIZEOF(long));
|
||||
} else {
|
||||
/* Compute derived values for Huffman tables */
|
||||
/* We may do this more than once for a table, but it's not expensive */
|
||||
if (is_DC_band)
|
||||
jpeg_make_c_derived_tbl(cinfo, cinfo->dc_huff_tbl_ptrs[tbl],
|
||||
& entropy->derived_tbls[tbl]);
|
||||
else
|
||||
jpeg_make_c_derived_tbl(cinfo, cinfo->ac_huff_tbl_ptrs[tbl],
|
||||
& entropy->derived_tbls[tbl]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize AC stuff */
|
||||
entropy->EOBRUN = 0;
|
||||
entropy->BE = 0;
|
||||
|
||||
/* Initialize bit buffer to empty */
|
||||
entropy->put_buffer = 0;
|
||||
entropy->put_bits = 0;
|
||||
|
||||
/* Initialize restart stuff */
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
entropy->next_restart_num = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Outputting bytes to the file.
|
||||
* NB: these must be called only when actually outputting,
|
||||
* that is, entropy->gather_statistics == FALSE.
|
||||
*/
|
||||
|
||||
/* Emit a byte */
|
||||
#define emit_byte(entropy,val) \
|
||||
{ *(entropy)->next_output_byte++ = (JOCTET) (val); \
|
||||
if (--(entropy)->free_in_buffer == 0) \
|
||||
dump_buffer(entropy); }
|
||||
|
||||
|
||||
LOCAL void
|
||||
dump_buffer (phuff_entropy_ptr entropy)
|
||||
/* Empty the output buffer; we do not support suspension in this module. */
|
||||
{
|
||||
struct jpeg_destination_mgr * dest = entropy->cinfo->dest;
|
||||
|
||||
if (! (*dest->empty_output_buffer) (entropy->cinfo))
|
||||
ERREXIT(entropy->cinfo, JERR_CANT_SUSPEND);
|
||||
/* After a successful buffer dump, must reset buffer pointers */
|
||||
entropy->next_output_byte = dest->next_output_byte;
|
||||
entropy->free_in_buffer = dest->free_in_buffer;
|
||||
}
|
||||
|
||||
|
||||
/* Outputting bits to the file */
|
||||
|
||||
/* Only the right 24 bits of put_buffer are used; the valid bits are
|
||||
* left-justified in this part. At most 16 bits can be passed to emit_bits
|
||||
* in one call, and we never retain more than 7 bits in put_buffer
|
||||
* between calls, so 24 bits are sufficient.
|
||||
*/
|
||||
|
||||
INLINE
|
||||
LOCAL void
|
||||
emit_bits (phuff_entropy_ptr entropy, unsigned int code, int size)
|
||||
/* Emit some bits, unless we are in gather mode */
|
||||
{
|
||||
/* This routine is heavily used, so it's worth coding tightly. */
|
||||
register INT32 put_buffer = (INT32) code;
|
||||
register int put_bits = entropy->put_bits;
|
||||
|
||||
/* if size is 0, caller used an invalid Huffman table entry */
|
||||
if (size == 0)
|
||||
ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE);
|
||||
|
||||
if (entropy->gather_statistics)
|
||||
return; /* do nothing if we're only getting stats */
|
||||
|
||||
put_buffer &= (((INT32) 1)<<size) - 1; /* mask off any extra bits in code */
|
||||
|
||||
put_bits += size; /* new number of bits in buffer */
|
||||
|
||||
put_buffer <<= 24 - put_bits; /* align incoming bits */
|
||||
|
||||
put_buffer |= entropy->put_buffer; /* and merge with old buffer contents */
|
||||
|
||||
while (put_bits >= 8) {
|
||||
int c = (int) ((put_buffer >> 16) & 0xFF);
|
||||
|
||||
emit_byte(entropy, c);
|
||||
if (c == 0xFF) { /* need to stuff a zero byte? */
|
||||
emit_byte(entropy, 0);
|
||||
}
|
||||
put_buffer <<= 8;
|
||||
put_bits -= 8;
|
||||
}
|
||||
|
||||
entropy->put_buffer = put_buffer; /* update variables */
|
||||
entropy->put_bits = put_bits;
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
flush_bits (phuff_entropy_ptr entropy)
|
||||
{
|
||||
emit_bits(entropy, 0x7F, 7); /* fill any partial byte with ones */
|
||||
entropy->put_buffer = 0; /* and reset bit-buffer to empty */
|
||||
entropy->put_bits = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Emit (or just count) a Huffman symbol.
|
||||
*/
|
||||
|
||||
INLINE
|
||||
LOCAL void
|
||||
emit_symbol (phuff_entropy_ptr entropy, int tbl_no, int symbol)
|
||||
{
|
||||
if (entropy->gather_statistics)
|
||||
entropy->count_ptrs[tbl_no][symbol]++;
|
||||
else {
|
||||
c_derived_tbl * tbl = entropy->derived_tbls[tbl_no];
|
||||
emit_bits(entropy, tbl->ehufco[symbol], tbl->ehufsi[symbol]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Emit bits from a correction bit buffer.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
emit_buffered_bits (phuff_entropy_ptr entropy, char * bufstart,
|
||||
unsigned int nbits)
|
||||
{
|
||||
if (entropy->gather_statistics)
|
||||
return; /* no real work */
|
||||
|
||||
while (nbits > 0) {
|
||||
emit_bits(entropy, (unsigned int) (*bufstart), 1);
|
||||
bufstart++;
|
||||
nbits--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Emit any pending EOBRUN symbol.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
emit_eobrun (phuff_entropy_ptr entropy)
|
||||
{
|
||||
register int temp, nbits;
|
||||
|
||||
if (entropy->EOBRUN > 0) { /* if there is any pending EOBRUN */
|
||||
temp = entropy->EOBRUN;
|
||||
nbits = 0;
|
||||
while ((temp >>= 1))
|
||||
nbits++;
|
||||
|
||||
emit_symbol(entropy, entropy->ac_tbl_no, nbits << 4);
|
||||
if (nbits)
|
||||
emit_bits(entropy, entropy->EOBRUN, nbits);
|
||||
|
||||
entropy->EOBRUN = 0;
|
||||
|
||||
/* Emit any buffered correction bits */
|
||||
emit_buffered_bits(entropy, entropy->bit_buffer, entropy->BE);
|
||||
entropy->BE = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Emit a restart marker & resynchronize predictions.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
emit_restart (phuff_entropy_ptr entropy, int restart_num)
|
||||
{
|
||||
int ci;
|
||||
|
||||
emit_eobrun(entropy);
|
||||
|
||||
if (! entropy->gather_statistics) {
|
||||
flush_bits(entropy);
|
||||
emit_byte(entropy, 0xFF);
|
||||
emit_byte(entropy, JPEG_RST0 + restart_num);
|
||||
}
|
||||
|
||||
if (entropy->cinfo->Ss == 0) {
|
||||
/* Re-initialize DC predictions to 0 */
|
||||
for (ci = 0; ci < entropy->cinfo->comps_in_scan; ci++)
|
||||
entropy->last_dc_val[ci] = 0;
|
||||
} else {
|
||||
/* Re-initialize all AC-related fields to 0 */
|
||||
entropy->EOBRUN = 0;
|
||||
entropy->BE = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MCU encoding for DC initial scan (either spectral selection,
|
||||
* or first pass of successive approximation).
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
register int temp, temp2;
|
||||
register int nbits;
|
||||
int blkn, ci;
|
||||
int Al = cinfo->Al;
|
||||
JBLOCKROW block;
|
||||
jpeg_component_info * compptr;
|
||||
ISHIFT_TEMPS
|
||||
|
||||
entropy->next_output_byte = cinfo->dest->next_output_byte;
|
||||
entropy->free_in_buffer = cinfo->dest->free_in_buffer;
|
||||
|
||||
/* Emit restart marker if needed */
|
||||
if (cinfo->restart_interval)
|
||||
if (entropy->restarts_to_go == 0)
|
||||
emit_restart(entropy, entropy->next_restart_num);
|
||||
|
||||
/* Encode the MCU data blocks */
|
||||
for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
|
||||
block = MCU_data[blkn];
|
||||
ci = cinfo->MCU_membership[blkn];
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
|
||||
/* Compute the DC value after the required point transform by Al.
|
||||
* This is simply an arithmetic right shift.
|
||||
*/
|
||||
temp2 = IRIGHT_SHIFT((int) ((*block)[0]), Al);
|
||||
|
||||
/* DC differences are figured on the point-transformed values. */
|
||||
temp = temp2 - entropy->last_dc_val[ci];
|
||||
entropy->last_dc_val[ci] = temp2;
|
||||
|
||||
/* Encode the DC coefficient difference per section G.1.2.1 */
|
||||
temp2 = temp;
|
||||
if (temp < 0) {
|
||||
temp = -temp; /* temp is abs value of input */
|
||||
/* For a negative input, want temp2 = bitwise complement of abs(input) */
|
||||
/* This code assumes we are on a two's complement machine */
|
||||
temp2--;
|
||||
}
|
||||
|
||||
/* Find the number of bits needed for the magnitude of the coefficient */
|
||||
nbits = 0;
|
||||
while (temp) {
|
||||
nbits++;
|
||||
temp >>= 1;
|
||||
}
|
||||
|
||||
/* Count/emit the Huffman-coded symbol for the number of bits */
|
||||
emit_symbol(entropy, compptr->dc_tbl_no, nbits);
|
||||
|
||||
/* Emit that number of bits of the value, if positive, */
|
||||
/* or the complement of its magnitude, if negative. */
|
||||
if (nbits) /* emit_bits rejects calls with size 0 */
|
||||
emit_bits(entropy, (unsigned int) temp2, nbits);
|
||||
}
|
||||
|
||||
cinfo->dest->next_output_byte = entropy->next_output_byte;
|
||||
cinfo->dest->free_in_buffer = entropy->free_in_buffer;
|
||||
|
||||
/* Update restart-interval state too */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0) {
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
entropy->next_restart_num++;
|
||||
entropy->next_restart_num &= 7;
|
||||
}
|
||||
entropy->restarts_to_go--;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MCU encoding for AC initial scan (either spectral selection,
|
||||
* or first pass of successive approximation).
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
register int temp, temp2;
|
||||
register int nbits;
|
||||
register int r, k;
|
||||
int Se = cinfo->Se;
|
||||
int Al = cinfo->Al;
|
||||
JBLOCKROW block;
|
||||
|
||||
entropy->next_output_byte = cinfo->dest->next_output_byte;
|
||||
entropy->free_in_buffer = cinfo->dest->free_in_buffer;
|
||||
|
||||
/* Emit restart marker if needed */
|
||||
if (cinfo->restart_interval)
|
||||
if (entropy->restarts_to_go == 0)
|
||||
emit_restart(entropy, entropy->next_restart_num);
|
||||
|
||||
/* Encode the MCU data block */
|
||||
block = MCU_data[0];
|
||||
|
||||
/* Encode the AC coefficients per section G.1.2.2, fig. G.3 */
|
||||
|
||||
r = 0; /* r = run length of zeros */
|
||||
|
||||
for (k = cinfo->Ss; k <= Se; k++) {
|
||||
if ((temp = (*block)[jpeg_natural_order[k]]) == 0) {
|
||||
r++;
|
||||
continue;
|
||||
}
|
||||
/* We must apply the point transform by Al. For AC coefficients this
|
||||
* is an integer division with rounding towards 0. To do this portably
|
||||
* in C, we shift after obtaining the absolute value; so the code is
|
||||
* interwoven with finding the abs value (temp) and output bits (temp2).
|
||||
*/
|
||||
if (temp < 0) {
|
||||
temp = -temp; /* temp is abs value of input */
|
||||
temp >>= Al; /* apply the point transform */
|
||||
/* For a negative coef, want temp2 = bitwise complement of abs(coef) */
|
||||
temp2 = ~temp;
|
||||
} else {
|
||||
temp >>= Al; /* apply the point transform */
|
||||
temp2 = temp;
|
||||
}
|
||||
/* Watch out for case that nonzero coef is zero after point transform */
|
||||
if (temp == 0) {
|
||||
r++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Emit any pending EOBRUN */
|
||||
if (entropy->EOBRUN > 0)
|
||||
emit_eobrun(entropy);
|
||||
/* if run length > 15, must emit special run-length-16 codes (0xF0) */
|
||||
while (r > 15) {
|
||||
emit_symbol(entropy, entropy->ac_tbl_no, 0xF0);
|
||||
r -= 16;
|
||||
}
|
||||
|
||||
/* Find the number of bits needed for the magnitude of the coefficient */
|
||||
nbits = 1; /* there must be at least one 1 bit */
|
||||
while ((temp >>= 1))
|
||||
nbits++;
|
||||
|
||||
/* Count/emit Huffman symbol for run length / number of bits */
|
||||
emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + nbits);
|
||||
|
||||
/* Emit that number of bits of the value, if positive, */
|
||||
/* or the complement of its magnitude, if negative. */
|
||||
emit_bits(entropy, (unsigned int) temp2, nbits);
|
||||
|
||||
r = 0; /* reset zero run length */
|
||||
}
|
||||
|
||||
if (r > 0) { /* If there are trailing zeroes, */
|
||||
entropy->EOBRUN++; /* count an EOB */
|
||||
if (entropy->EOBRUN == 0x7FFF)
|
||||
emit_eobrun(entropy); /* force it out to avoid overflow */
|
||||
}
|
||||
|
||||
cinfo->dest->next_output_byte = entropy->next_output_byte;
|
||||
cinfo->dest->free_in_buffer = entropy->free_in_buffer;
|
||||
|
||||
/* Update restart-interval state too */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0) {
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
entropy->next_restart_num++;
|
||||
entropy->next_restart_num &= 7;
|
||||
}
|
||||
entropy->restarts_to_go--;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MCU encoding for DC successive approximation refinement scan.
|
||||
* Note: we assume such scans can be multi-component, although the spec
|
||||
* is not very clear on the point.
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
register int temp;
|
||||
int blkn;
|
||||
int Al = cinfo->Al;
|
||||
JBLOCKROW block;
|
||||
|
||||
entropy->next_output_byte = cinfo->dest->next_output_byte;
|
||||
entropy->free_in_buffer = cinfo->dest->free_in_buffer;
|
||||
|
||||
/* Emit restart marker if needed */
|
||||
if (cinfo->restart_interval)
|
||||
if (entropy->restarts_to_go == 0)
|
||||
emit_restart(entropy, entropy->next_restart_num);
|
||||
|
||||
/* Encode the MCU data blocks */
|
||||
for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
|
||||
block = MCU_data[blkn];
|
||||
|
||||
/* We simply emit the Al'th bit of the DC coefficient value. */
|
||||
temp = (*block)[0];
|
||||
emit_bits(entropy, (unsigned int) (temp >> Al), 1);
|
||||
}
|
||||
|
||||
cinfo->dest->next_output_byte = entropy->next_output_byte;
|
||||
cinfo->dest->free_in_buffer = entropy->free_in_buffer;
|
||||
|
||||
/* Update restart-interval state too */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0) {
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
entropy->next_restart_num++;
|
||||
entropy->next_restart_num &= 7;
|
||||
}
|
||||
entropy->restarts_to_go--;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MCU encoding for AC successive approximation refinement scan.
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
register int temp;
|
||||
register int r, k;
|
||||
int EOB;
|
||||
char *BR_buffer;
|
||||
unsigned int BR;
|
||||
int Se = cinfo->Se;
|
||||
int Al = cinfo->Al;
|
||||
JBLOCKROW block;
|
||||
int absvalues[DCTSIZE2];
|
||||
|
||||
entropy->next_output_byte = cinfo->dest->next_output_byte;
|
||||
entropy->free_in_buffer = cinfo->dest->free_in_buffer;
|
||||
|
||||
/* Emit restart marker if needed */
|
||||
if (cinfo->restart_interval)
|
||||
if (entropy->restarts_to_go == 0)
|
||||
emit_restart(entropy, entropy->next_restart_num);
|
||||
|
||||
/* Encode the MCU data block */
|
||||
block = MCU_data[0];
|
||||
|
||||
/* It is convenient to make a pre-pass to determine the transformed
|
||||
* coefficients' absolute values and the EOB position.
|
||||
*/
|
||||
EOB = 0;
|
||||
for (k = cinfo->Ss; k <= Se; k++) {
|
||||
temp = (*block)[jpeg_natural_order[k]];
|
||||
/* We must apply the point transform by Al. For AC coefficients this
|
||||
* is an integer division with rounding towards 0. To do this portably
|
||||
* in C, we shift after obtaining the absolute value.
|
||||
*/
|
||||
if (temp < 0)
|
||||
temp = -temp; /* temp is abs value of input */
|
||||
temp >>= Al; /* apply the point transform */
|
||||
absvalues[k] = temp; /* save abs value for main pass */
|
||||
if (temp == 1)
|
||||
EOB = k; /* EOB = index of last newly-nonzero coef */
|
||||
}
|
||||
|
||||
/* Encode the AC coefficients per section G.1.2.3, fig. G.7 */
|
||||
|
||||
r = 0; /* r = run length of zeros */
|
||||
BR = 0; /* BR = count of buffered bits added now */
|
||||
BR_buffer = entropy->bit_buffer + entropy->BE; /* Append bits to buffer */
|
||||
|
||||
for (k = cinfo->Ss; k <= Se; k++) {
|
||||
if ((temp = absvalues[k]) == 0) {
|
||||
r++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Emit any required ZRLs, but not if they can be folded into EOB */
|
||||
while (r > 15 && k <= EOB) {
|
||||
/* emit any pending EOBRUN and the BE correction bits */
|
||||
emit_eobrun(entropy);
|
||||
/* Emit ZRL */
|
||||
emit_symbol(entropy, entropy->ac_tbl_no, 0xF0);
|
||||
r -= 16;
|
||||
/* Emit buffered correction bits that must be associated with ZRL */
|
||||
emit_buffered_bits(entropy, BR_buffer, BR);
|
||||
BR_buffer = entropy->bit_buffer; /* BE bits are gone now */
|
||||
BR = 0;
|
||||
}
|
||||
|
||||
/* If the coef was previously nonzero, it only needs a correction bit.
|
||||
* NOTE: a straight translation of the spec's figure G.7 would suggest
|
||||
* that we also need to test r > 15. But if r > 15, we can only get here
|
||||
* if k > EOB, which implies that this coefficient is not 1.
|
||||
*/
|
||||
if (temp > 1) {
|
||||
/* The correction bit is the next bit of the absolute value. */
|
||||
BR_buffer[BR++] = (char) (temp & 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Emit any pending EOBRUN and the BE correction bits */
|
||||
emit_eobrun(entropy);
|
||||
|
||||
/* Count/emit Huffman symbol for run length / number of bits */
|
||||
emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + 1);
|
||||
|
||||
/* Emit output bit for newly-nonzero coef */
|
||||
temp = ((*block)[jpeg_natural_order[k]] < 0) ? 0 : 1;
|
||||
emit_bits(entropy, (unsigned int) temp, 1);
|
||||
|
||||
/* Emit buffered correction bits that must be associated with this code */
|
||||
emit_buffered_bits(entropy, BR_buffer, BR);
|
||||
BR_buffer = entropy->bit_buffer; /* BE bits are gone now */
|
||||
BR = 0;
|
||||
r = 0; /* reset zero run length */
|
||||
}
|
||||
|
||||
if (r > 0 || BR > 0) { /* If there are trailing zeroes, */
|
||||
entropy->EOBRUN++; /* count an EOB */
|
||||
entropy->BE += BR; /* concat my correction bits to older ones */
|
||||
/* We force out the EOB if we risk either:
|
||||
* 1. overflow of the EOB counter;
|
||||
* 2. overflow of the correction bit buffer during the next MCU.
|
||||
*/
|
||||
if (entropy->EOBRUN == 0x7FFF || entropy->BE > (MAX_CORR_BITS-DCTSIZE2+1))
|
||||
emit_eobrun(entropy);
|
||||
}
|
||||
|
||||
cinfo->dest->next_output_byte = entropy->next_output_byte;
|
||||
cinfo->dest->free_in_buffer = entropy->free_in_buffer;
|
||||
|
||||
/* Update restart-interval state too */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0) {
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
entropy->next_restart_num++;
|
||||
entropy->next_restart_num &= 7;
|
||||
}
|
||||
entropy->restarts_to_go--;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish up at the end of a Huffman-compressed progressive scan.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
finish_pass_phuff (j_compress_ptr cinfo)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
|
||||
entropy->next_output_byte = cinfo->dest->next_output_byte;
|
||||
entropy->free_in_buffer = cinfo->dest->free_in_buffer;
|
||||
|
||||
/* Flush out any buffered data */
|
||||
emit_eobrun(entropy);
|
||||
flush_bits(entropy);
|
||||
|
||||
cinfo->dest->next_output_byte = entropy->next_output_byte;
|
||||
cinfo->dest->free_in_buffer = entropy->free_in_buffer;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish up a statistics-gathering pass and create the new Huffman tables.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
finish_pass_gather_phuff (j_compress_ptr cinfo)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
boolean is_DC_band;
|
||||
int ci, tbl;
|
||||
jpeg_component_info * compptr;
|
||||
JHUFF_TBL **htblptr;
|
||||
boolean did[NUM_HUFF_TBLS];
|
||||
|
||||
/* Flush out buffered data (all we care about is counting the EOB symbol) */
|
||||
emit_eobrun(entropy);
|
||||
|
||||
is_DC_band = (cinfo->Ss == 0);
|
||||
|
||||
/* It's important not to apply jpeg_gen_optimal_table more than once
|
||||
* per table, because it clobbers the input frequency counts!
|
||||
*/
|
||||
MEMZERO(did, SIZEOF(did));
|
||||
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
if (is_DC_band) {
|
||||
if (cinfo->Ah != 0) /* DC refinement needs no table */
|
||||
continue;
|
||||
tbl = compptr->dc_tbl_no;
|
||||
} else {
|
||||
tbl = compptr->ac_tbl_no;
|
||||
}
|
||||
if (! did[tbl]) {
|
||||
if (is_DC_band)
|
||||
htblptr = & cinfo->dc_huff_tbl_ptrs[tbl];
|
||||
else
|
||||
htblptr = & cinfo->ac_huff_tbl_ptrs[tbl];
|
||||
if (*htblptr == NULL)
|
||||
*htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo);
|
||||
jpeg_gen_optimal_table(cinfo, *htblptr, entropy->count_ptrs[tbl]);
|
||||
did[tbl] = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Module initialization routine for progressive Huffman entropy encoding.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_phuff_encoder (j_compress_ptr cinfo)
|
||||
{
|
||||
phuff_entropy_ptr entropy;
|
||||
int i;
|
||||
|
||||
entropy = (phuff_entropy_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(phuff_entropy_encoder));
|
||||
cinfo->entropy = (struct jpeg_entropy_encoder *) entropy;
|
||||
entropy->pub.start_pass = start_pass_phuff;
|
||||
|
||||
/* Mark tables unallocated */
|
||||
for (i = 0; i < NUM_HUFF_TBLS; i++) {
|
||||
entropy->derived_tbls[i] = NULL;
|
||||
entropy->count_ptrs[i] = NULL;
|
||||
}
|
||||
entropy->bit_buffer = NULL; /* needed only in AC refinement scan */
|
||||
}
|
||||
|
||||
#endif /* C_PROGRESSIVE_SUPPORTED */
|
||||
371
neo/renderer/jpeg-6/jcprepct.c
Normal file
371
neo/renderer/jpeg-6/jcprepct.c
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* jcprepct.c
|
||||
*
|
||||
* Copyright (C) 1994, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains the compression preprocessing controller.
|
||||
* This controller manages the color conversion, downsampling,
|
||||
* and edge expansion steps.
|
||||
*
|
||||
* Most of the complexity here is associated with buffering input rows
|
||||
* as required by the downsampler. See the comments at the head of
|
||||
* jcsample.c for the downsampler's needs.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* At present, jcsample.c can request context rows only for smoothing.
|
||||
* In the future, we might also need context rows for CCIR601 sampling
|
||||
* or other more-complex downsampling procedures. The code to support
|
||||
* context rows should be compiled only if needed.
|
||||
*/
|
||||
#ifdef INPUT_SMOOTHING_SUPPORTED
|
||||
#define CONTEXT_ROWS_SUPPORTED
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* For the simple (no-context-row) case, we just need to buffer one
|
||||
* row group's worth of pixels for the downsampling step. At the bottom of
|
||||
* the image, we pad to a full row group by replicating the last pixel row.
|
||||
* The downsampler's last output row is then replicated if needed to pad
|
||||
* out to a full iMCU row.
|
||||
*
|
||||
* When providing context rows, we must buffer three row groups' worth of
|
||||
* pixels. Three row groups are physically allocated, but the row pointer
|
||||
* arrays are made five row groups high, with the extra pointers above and
|
||||
* below "wrapping around" to point to the last and first real row groups.
|
||||
* This allows the downsampler to access the proper context rows.
|
||||
* At the top and bottom of the image, we create dummy context rows by
|
||||
* copying the first or last real pixel row. This copying could be avoided
|
||||
* by pointer hacking as is done in jdmainct.c, but it doesn't seem worth the
|
||||
* trouble on the compression side.
|
||||
*/
|
||||
|
||||
|
||||
/* Private buffer controller object */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_c_prep_controller pub; /* public fields */
|
||||
|
||||
/* Downsampling input buffer. This buffer holds color-converted data
|
||||
* until we have enough to do a downsample step.
|
||||
*/
|
||||
JSAMPARRAY color_buf[MAX_COMPONENTS];
|
||||
|
||||
JDIMENSION rows_to_go; /* counts rows remaining in source image */
|
||||
int next_buf_row; /* index of next row to store in color_buf */
|
||||
|
||||
#ifdef CONTEXT_ROWS_SUPPORTED /* only needed for context case */
|
||||
int this_row_group; /* starting row index of group to process */
|
||||
int next_buf_stop; /* downsample when we reach this index */
|
||||
#endif
|
||||
} my_prep_controller;
|
||||
|
||||
typedef my_prep_controller * my_prep_ptr;
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a processing pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_prep (j_compress_ptr cinfo, J_BUF_MODE pass_mode)
|
||||
{
|
||||
my_prep_ptr prep = (my_prep_ptr) cinfo->prep;
|
||||
|
||||
if (pass_mode != JBUF_PASS_THRU)
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
|
||||
/* Initialize total-height counter for detecting bottom of image */
|
||||
prep->rows_to_go = cinfo->image_height;
|
||||
/* Mark the conversion buffer empty */
|
||||
prep->next_buf_row = 0;
|
||||
#ifdef CONTEXT_ROWS_SUPPORTED
|
||||
/* Preset additional state variables for context mode.
|
||||
* These aren't used in non-context mode, so we needn't test which mode.
|
||||
*/
|
||||
prep->this_row_group = 0;
|
||||
/* Set next_buf_stop to stop after two row groups have been read in. */
|
||||
prep->next_buf_stop = 2 * cinfo->max_v_samp_factor;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Expand an image vertically from height input_rows to height output_rows,
|
||||
* by duplicating the bottom row.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
expand_bottom_edge (JSAMPARRAY image_data, JDIMENSION num_cols,
|
||||
int input_rows, int output_rows)
|
||||
{
|
||||
register int row;
|
||||
|
||||
for (row = input_rows; row < output_rows; row++) {
|
||||
jcopy_sample_rows(image_data, input_rows-1, image_data, row,
|
||||
1, num_cols);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process some data in the simple no-context case.
|
||||
*
|
||||
* Preprocessor output data is counted in "row groups". A row group
|
||||
* is defined to be v_samp_factor sample rows of each component.
|
||||
* Downsampling will produce this much data from each max_v_samp_factor
|
||||
* input rows.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
pre_process_data (j_compress_ptr cinfo,
|
||||
JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
|
||||
JDIMENSION in_rows_avail,
|
||||
JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr,
|
||||
JDIMENSION out_row_groups_avail)
|
||||
{
|
||||
my_prep_ptr prep = (my_prep_ptr) cinfo->prep;
|
||||
int numrows, ci;
|
||||
JDIMENSION inrows;
|
||||
jpeg_component_info * compptr;
|
||||
|
||||
while (*in_row_ctr < in_rows_avail &&
|
||||
*out_row_group_ctr < out_row_groups_avail) {
|
||||
/* Do color conversion to fill the conversion buffer. */
|
||||
inrows = in_rows_avail - *in_row_ctr;
|
||||
numrows = cinfo->max_v_samp_factor - prep->next_buf_row;
|
||||
numrows = (int) MIN((JDIMENSION) numrows, inrows);
|
||||
(*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr,
|
||||
prep->color_buf,
|
||||
(JDIMENSION) prep->next_buf_row,
|
||||
numrows);
|
||||
*in_row_ctr += numrows;
|
||||
prep->next_buf_row += numrows;
|
||||
prep->rows_to_go -= numrows;
|
||||
/* If at bottom of image, pad to fill the conversion buffer. */
|
||||
if (prep->rows_to_go == 0 &&
|
||||
prep->next_buf_row < cinfo->max_v_samp_factor) {
|
||||
for (ci = 0; ci < cinfo->num_components; ci++) {
|
||||
expand_bottom_edge(prep->color_buf[ci], cinfo->image_width,
|
||||
prep->next_buf_row, cinfo->max_v_samp_factor);
|
||||
}
|
||||
prep->next_buf_row = cinfo->max_v_samp_factor;
|
||||
}
|
||||
/* If we've filled the conversion buffer, empty it. */
|
||||
if (prep->next_buf_row == cinfo->max_v_samp_factor) {
|
||||
(*cinfo->downsample->downsample) (cinfo,
|
||||
prep->color_buf, (JDIMENSION) 0,
|
||||
output_buf, *out_row_group_ctr);
|
||||
prep->next_buf_row = 0;
|
||||
(*out_row_group_ctr)++;
|
||||
}
|
||||
/* If at bottom of image, pad the output to a full iMCU height.
|
||||
* Note we assume the caller is providing a one-iMCU-height output buffer!
|
||||
*/
|
||||
if (prep->rows_to_go == 0 &&
|
||||
*out_row_group_ctr < out_row_groups_avail) {
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
expand_bottom_edge(output_buf[ci],
|
||||
compptr->width_in_blocks * DCTSIZE,
|
||||
(int) (*out_row_group_ctr * compptr->v_samp_factor),
|
||||
(int) (out_row_groups_avail * compptr->v_samp_factor));
|
||||
}
|
||||
*out_row_group_ctr = out_row_groups_avail;
|
||||
break; /* can exit outer loop without test */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONTEXT_ROWS_SUPPORTED
|
||||
|
||||
/*
|
||||
* Process some data in the context case.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
pre_process_context (j_compress_ptr cinfo,
|
||||
JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
|
||||
JDIMENSION in_rows_avail,
|
||||
JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr,
|
||||
JDIMENSION out_row_groups_avail)
|
||||
{
|
||||
my_prep_ptr prep = (my_prep_ptr) cinfo->prep;
|
||||
int numrows, ci;
|
||||
int buf_height = cinfo->max_v_samp_factor * 3;
|
||||
JDIMENSION inrows;
|
||||
jpeg_component_info * compptr;
|
||||
|
||||
while (*out_row_group_ctr < out_row_groups_avail) {
|
||||
if (*in_row_ctr < in_rows_avail) {
|
||||
/* Do color conversion to fill the conversion buffer. */
|
||||
inrows = in_rows_avail - *in_row_ctr;
|
||||
numrows = prep->next_buf_stop - prep->next_buf_row;
|
||||
numrows = (int) MIN((JDIMENSION) numrows, inrows);
|
||||
(*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr,
|
||||
prep->color_buf,
|
||||
(JDIMENSION) prep->next_buf_row,
|
||||
numrows);
|
||||
/* Pad at top of image, if first time through */
|
||||
if (prep->rows_to_go == cinfo->image_height) {
|
||||
for (ci = 0; ci < cinfo->num_components; ci++) {
|
||||
int row;
|
||||
for (row = 1; row <= cinfo->max_v_samp_factor; row++) {
|
||||
jcopy_sample_rows(prep->color_buf[ci], 0,
|
||||
prep->color_buf[ci], -row,
|
||||
1, cinfo->image_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
*in_row_ctr += numrows;
|
||||
prep->next_buf_row += numrows;
|
||||
prep->rows_to_go -= numrows;
|
||||
} else {
|
||||
/* Return for more data, unless we are at the bottom of the image. */
|
||||
if (prep->rows_to_go != 0)
|
||||
break;
|
||||
}
|
||||
/* If at bottom of image, pad to fill the conversion buffer. */
|
||||
if (prep->rows_to_go == 0 &&
|
||||
prep->next_buf_row < prep->next_buf_stop) {
|
||||
for (ci = 0; ci < cinfo->num_components; ci++) {
|
||||
expand_bottom_edge(prep->color_buf[ci], cinfo->image_width,
|
||||
prep->next_buf_row, prep->next_buf_stop);
|
||||
}
|
||||
prep->next_buf_row = prep->next_buf_stop;
|
||||
}
|
||||
/* If we've gotten enough data, downsample a row group. */
|
||||
if (prep->next_buf_row == prep->next_buf_stop) {
|
||||
(*cinfo->downsample->downsample) (cinfo,
|
||||
prep->color_buf,
|
||||
(JDIMENSION) prep->this_row_group,
|
||||
output_buf, *out_row_group_ctr);
|
||||
(*out_row_group_ctr)++;
|
||||
/* Advance pointers with wraparound as necessary. */
|
||||
prep->this_row_group += cinfo->max_v_samp_factor;
|
||||
if (prep->this_row_group >= buf_height)
|
||||
prep->this_row_group = 0;
|
||||
if (prep->next_buf_row >= buf_height)
|
||||
prep->next_buf_row = 0;
|
||||
prep->next_buf_stop = prep->next_buf_row + cinfo->max_v_samp_factor;
|
||||
}
|
||||
/* If at bottom of image, pad the output to a full iMCU height.
|
||||
* Note we assume the caller is providing a one-iMCU-height output buffer!
|
||||
*/
|
||||
if (prep->rows_to_go == 0 &&
|
||||
*out_row_group_ctr < out_row_groups_avail) {
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
expand_bottom_edge(output_buf[ci],
|
||||
compptr->width_in_blocks * DCTSIZE,
|
||||
(int) (*out_row_group_ctr * compptr->v_samp_factor),
|
||||
(int) (out_row_groups_avail * compptr->v_samp_factor));
|
||||
}
|
||||
*out_row_group_ctr = out_row_groups_avail;
|
||||
break; /* can exit outer loop without test */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create the wrapped-around downsampling input buffer needed for context mode.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
create_context_buffer (j_compress_ptr cinfo)
|
||||
{
|
||||
my_prep_ptr prep = (my_prep_ptr) cinfo->prep;
|
||||
int rgroup_height = cinfo->max_v_samp_factor;
|
||||
int ci, i;
|
||||
jpeg_component_info * compptr;
|
||||
JSAMPARRAY true_buffer, fake_buffer;
|
||||
|
||||
/* Grab enough space for fake row pointers for all the components;
|
||||
* we need five row groups' worth of pointers for each component.
|
||||
*/
|
||||
fake_buffer = (JSAMPARRAY)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(cinfo->num_components * 5 * rgroup_height) *
|
||||
SIZEOF(JSAMPROW));
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* Allocate the actual buffer space (3 row groups) for this component.
|
||||
* We make the buffer wide enough to allow the downsampler to edge-expand
|
||||
* horizontally within the buffer, if it so chooses.
|
||||
*/
|
||||
true_buffer = (*cinfo->mem->alloc_sarray)
|
||||
((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE *
|
||||
cinfo->max_h_samp_factor) / compptr->h_samp_factor),
|
||||
(JDIMENSION) (3 * rgroup_height));
|
||||
/* Copy true buffer row pointers into the middle of the fake row array */
|
||||
MEMCOPY(fake_buffer + rgroup_height, true_buffer,
|
||||
3 * rgroup_height * SIZEOF(JSAMPROW));
|
||||
/* Fill in the above and below wraparound pointers */
|
||||
for (i = 0; i < rgroup_height; i++) {
|
||||
fake_buffer[i] = true_buffer[2 * rgroup_height + i];
|
||||
fake_buffer[4 * rgroup_height + i] = true_buffer[i];
|
||||
}
|
||||
prep->color_buf[ci] = fake_buffer + rgroup_height;
|
||||
fake_buffer += 5 * rgroup_height; /* point to space for next component */
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONTEXT_ROWS_SUPPORTED */
|
||||
|
||||
|
||||
/*
|
||||
* Initialize preprocessing controller.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer)
|
||||
{
|
||||
my_prep_ptr prep;
|
||||
int ci;
|
||||
jpeg_component_info * compptr;
|
||||
|
||||
if (need_full_buffer) /* safety check */
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
|
||||
prep = (my_prep_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_prep_controller));
|
||||
cinfo->prep = (struct jpeg_c_prep_controller *) prep;
|
||||
prep->pub.start_pass = start_pass_prep;
|
||||
|
||||
/* Allocate the color conversion buffer.
|
||||
* We make the buffer wide enough to allow the downsampler to edge-expand
|
||||
* horizontally within the buffer, if it so chooses.
|
||||
*/
|
||||
if (cinfo->downsample->need_context_rows) {
|
||||
/* Set up to provide context rows */
|
||||
#ifdef CONTEXT_ROWS_SUPPORTED
|
||||
prep->pub.pre_process_data = pre_process_context;
|
||||
create_context_buffer(cinfo);
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
} else {
|
||||
/* No context, just make it tall enough for one row group */
|
||||
prep->pub.pre_process_data = pre_process_data;
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
prep->color_buf[ci] = (*cinfo->mem->alloc_sarray)
|
||||
((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE *
|
||||
cinfo->max_h_samp_factor) / compptr->h_samp_factor),
|
||||
(JDIMENSION) cinfo->max_v_samp_factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
519
neo/renderer/jpeg-6/jcsample.c
Normal file
519
neo/renderer/jpeg-6/jcsample.c
Normal file
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* jcsample.c
|
||||
*
|
||||
* Copyright (C) 1991-1994, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains downsampling routines.
|
||||
*
|
||||
* Downsampling input data is counted in "row groups". A row group
|
||||
* is defined to be max_v_samp_factor pixel rows of each component,
|
||||
* from which the downsampler produces v_samp_factor sample rows.
|
||||
* A single row group is processed in each call to the downsampler module.
|
||||
*
|
||||
* The downsampler is responsible for edge-expansion of its output data
|
||||
* to fill an integral number of DCT blocks horizontally. The source buffer
|
||||
* may be modified if it is helpful for this purpose (the source buffer is
|
||||
* allocated wide enough to correspond to the desired output width).
|
||||
* The caller (the prep controller) is responsible for vertical padding.
|
||||
*
|
||||
* The downsampler may request "context rows" by setting need_context_rows
|
||||
* during startup. In this case, the input arrays will contain at least
|
||||
* one row group's worth of pixels above and below the passed-in data;
|
||||
* the caller will create dummy rows at image top and bottom by replicating
|
||||
* the first or last real pixel row.
|
||||
*
|
||||
* An excellent reference for image resampling is
|
||||
* Digital Image Warping, George Wolberg, 1990.
|
||||
* Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7.
|
||||
*
|
||||
* The downsampling algorithm used here is a simple average of the source
|
||||
* pixels covered by the output pixel. The hi-falutin sampling literature
|
||||
* refers to this as a "box filter". In general the characteristics of a box
|
||||
* filter are not very good, but for the specific cases we normally use (1:1
|
||||
* and 2:1 ratios) the box is equivalent to a "triangle filter" which is not
|
||||
* nearly so bad. If you intend to use other sampling ratios, you'd be well
|
||||
* advised to improve this code.
|
||||
*
|
||||
* A simple input-smoothing capability is provided. This is mainly intended
|
||||
* for cleaning up color-dithered GIF input files (if you find it inadequate,
|
||||
* we suggest using an external filtering program such as pnmconvol). When
|
||||
* enabled, each input pixel P is replaced by a weighted sum of itself and its
|
||||
* eight neighbors. P's weight is 1-8*SF and each neighbor's weight is SF,
|
||||
* where SF = (smoothing_factor / 1024).
|
||||
* Currently, smoothing is only supported for 2h2v sampling factors.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Pointer to routine to downsample a single component */
|
||||
typedef JMETHOD(void, downsample1_ptr,
|
||||
(j_compress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY output_data));
|
||||
|
||||
/* Private subobject */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_downsampler pub; /* public fields */
|
||||
|
||||
/* Downsampling method pointers, one per component */
|
||||
downsample1_ptr methods[MAX_COMPONENTS];
|
||||
} my_downsampler;
|
||||
|
||||
typedef my_downsampler * my_downsample_ptr;
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a downsampling pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_downsample (j_compress_ptr cinfo)
|
||||
{
|
||||
/* no work for now */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Expand a component horizontally from width input_cols to width output_cols,
|
||||
* by duplicating the rightmost samples.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
expand_right_edge (JSAMPARRAY image_data, int num_rows,
|
||||
JDIMENSION input_cols, JDIMENSION output_cols)
|
||||
{
|
||||
register JSAMPROW ptr;
|
||||
register JSAMPLE pixval;
|
||||
register int count;
|
||||
int row;
|
||||
int numcols = (int) (output_cols - input_cols);
|
||||
|
||||
if (numcols > 0) {
|
||||
for (row = 0; row < num_rows; row++) {
|
||||
ptr = image_data[row] + input_cols;
|
||||
pixval = ptr[-1]; /* don't need GETJSAMPLE() here */
|
||||
for (count = numcols; count > 0; count--)
|
||||
*ptr++ = pixval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Do downsampling for a whole row group (all components).
|
||||
*
|
||||
* In this version we simply downsample each component independently.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
sep_downsample (j_compress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION in_row_index,
|
||||
JSAMPIMAGE output_buf, JDIMENSION out_row_group_index)
|
||||
{
|
||||
my_downsample_ptr downsample = (my_downsample_ptr) cinfo->downsample;
|
||||
int ci;
|
||||
jpeg_component_info * compptr;
|
||||
JSAMPARRAY in_ptr, out_ptr;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
in_ptr = input_buf[ci] + in_row_index;
|
||||
out_ptr = output_buf[ci] + (out_row_group_index * compptr->v_samp_factor);
|
||||
(*downsample->methods[ci]) (cinfo, compptr, in_ptr, out_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Downsample pixel values of a single component.
|
||||
* One row group is processed per call.
|
||||
* This version handles arbitrary integral sampling ratios, without smoothing.
|
||||
* Note that this version is not actually used for customary sampling ratios.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
int_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY output_data)
|
||||
{
|
||||
int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v;
|
||||
JDIMENSION outcol, outcol_h; /* outcol_h == outcol*h_expand */
|
||||
JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
|
||||
JSAMPROW inptr, outptr;
|
||||
INT32 outvalue;
|
||||
|
||||
h_expand = cinfo->max_h_samp_factor / compptr->h_samp_factor;
|
||||
v_expand = cinfo->max_v_samp_factor / compptr->v_samp_factor;
|
||||
numpix = h_expand * v_expand;
|
||||
numpix2 = numpix/2;
|
||||
|
||||
/* Expand input data enough to let all the output samples be generated
|
||||
* by the standard loop. Special-casing padded output would be more
|
||||
* efficient.
|
||||
*/
|
||||
expand_right_edge(input_data, cinfo->max_v_samp_factor,
|
||||
cinfo->image_width, output_cols * h_expand);
|
||||
|
||||
inrow = 0;
|
||||
for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) {
|
||||
outptr = output_data[outrow];
|
||||
for (outcol = 0, outcol_h = 0; outcol < output_cols;
|
||||
outcol++, outcol_h += h_expand) {
|
||||
outvalue = 0;
|
||||
for (v = 0; v < v_expand; v++) {
|
||||
inptr = input_data[inrow+v] + outcol_h;
|
||||
for (h = 0; h < h_expand; h++) {
|
||||
outvalue += (INT32) GETJSAMPLE(*inptr++);
|
||||
}
|
||||
}
|
||||
*outptr++ = (JSAMPLE) ((outvalue + numpix2) / numpix);
|
||||
}
|
||||
inrow += v_expand;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Downsample pixel values of a single component.
|
||||
* This version handles the special case of a full-size component,
|
||||
* without smoothing.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
fullsize_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY output_data)
|
||||
{
|
||||
/* Copy the data */
|
||||
jcopy_sample_rows(input_data, 0, output_data, 0,
|
||||
cinfo->max_v_samp_factor, cinfo->image_width);
|
||||
/* Edge-expand */
|
||||
expand_right_edge(output_data, cinfo->max_v_samp_factor,
|
||||
cinfo->image_width, compptr->width_in_blocks * DCTSIZE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Downsample pixel values of a single component.
|
||||
* This version handles the common case of 2:1 horizontal and 1:1 vertical,
|
||||
* without smoothing.
|
||||
*
|
||||
* A note about the "bias" calculations: when rounding fractional values to
|
||||
* integer, we do not want to always round 0.5 up to the next integer.
|
||||
* If we did that, we'd introduce a noticeable bias towards larger values.
|
||||
* Instead, this code is arranged so that 0.5 will be rounded up or down at
|
||||
* alternate pixel locations (a simple ordered dither pattern).
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY output_data)
|
||||
{
|
||||
int outrow;
|
||||
JDIMENSION outcol;
|
||||
JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
|
||||
register JSAMPROW inptr, outptr;
|
||||
register int bias;
|
||||
|
||||
/* Expand input data enough to let all the output samples be generated
|
||||
* by the standard loop. Special-casing padded output would be more
|
||||
* efficient.
|
||||
*/
|
||||
expand_right_edge(input_data, cinfo->max_v_samp_factor,
|
||||
cinfo->image_width, output_cols * 2);
|
||||
|
||||
for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) {
|
||||
outptr = output_data[outrow];
|
||||
inptr = input_data[outrow];
|
||||
bias = 0; /* bias = 0,1,0,1,... for successive samples */
|
||||
for (outcol = 0; outcol < output_cols; outcol++) {
|
||||
*outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr) + GETJSAMPLE(inptr[1])
|
||||
+ bias) >> 1);
|
||||
bias ^= 1; /* 0=>1, 1=>0 */
|
||||
inptr += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Downsample pixel values of a single component.
|
||||
* This version handles the standard case of 2:1 horizontal and 2:1 vertical,
|
||||
* without smoothing.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY output_data)
|
||||
{
|
||||
int inrow, outrow;
|
||||
JDIMENSION outcol;
|
||||
JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
|
||||
register JSAMPROW inptr0, inptr1, outptr;
|
||||
register int bias;
|
||||
|
||||
/* Expand input data enough to let all the output samples be generated
|
||||
* by the standard loop. Special-casing padded output would be more
|
||||
* efficient.
|
||||
*/
|
||||
expand_right_edge(input_data, cinfo->max_v_samp_factor,
|
||||
cinfo->image_width, output_cols * 2);
|
||||
|
||||
inrow = 0;
|
||||
for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) {
|
||||
outptr = output_data[outrow];
|
||||
inptr0 = input_data[inrow];
|
||||
inptr1 = input_data[inrow+1];
|
||||
bias = 1; /* bias = 1,2,1,2,... for successive samples */
|
||||
for (outcol = 0; outcol < output_cols; outcol++) {
|
||||
*outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) +
|
||||
GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1])
|
||||
+ bias) >> 2);
|
||||
bias ^= 3; /* 1=>2, 2=>1 */
|
||||
inptr0 += 2; inptr1 += 2;
|
||||
}
|
||||
inrow += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef INPUT_SMOOTHING_SUPPORTED
|
||||
|
||||
/*
|
||||
* Downsample pixel values of a single component.
|
||||
* This version handles the standard case of 2:1 horizontal and 2:1 vertical,
|
||||
* with smoothing. One row of context is required.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY output_data)
|
||||
{
|
||||
int inrow, outrow;
|
||||
JDIMENSION colctr;
|
||||
JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
|
||||
register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr;
|
||||
INT32 membersum, neighsum, memberscale, neighscale;
|
||||
|
||||
/* Expand input data enough to let all the output samples be generated
|
||||
* by the standard loop. Special-casing padded output would be more
|
||||
* efficient.
|
||||
*/
|
||||
expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2,
|
||||
cinfo->image_width, output_cols * 2);
|
||||
|
||||
/* We don't bother to form the individual "smoothed" input pixel values;
|
||||
* we can directly compute the output which is the average of the four
|
||||
* smoothed values. Each of the four member pixels contributes a fraction
|
||||
* (1-8*SF) to its own smoothed image and a fraction SF to each of the three
|
||||
* other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final
|
||||
* output. The four corner-adjacent neighbor pixels contribute a fraction
|
||||
* SF to just one smoothed pixel, or SF/4 to the final output; while the
|
||||
* eight edge-adjacent neighbors contribute SF to each of two smoothed
|
||||
* pixels, or SF/2 overall. In order to use integer arithmetic, these
|
||||
* factors are scaled by 2^16 = 65536.
|
||||
* Also recall that SF = smoothing_factor / 1024.
|
||||
*/
|
||||
|
||||
memberscale = 16384 - cinfo->smoothing_factor * 80; /* scaled (1-5*SF)/4 */
|
||||
neighscale = cinfo->smoothing_factor * 16; /* scaled SF/4 */
|
||||
|
||||
inrow = 0;
|
||||
for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) {
|
||||
outptr = output_data[outrow];
|
||||
inptr0 = input_data[inrow];
|
||||
inptr1 = input_data[inrow+1];
|
||||
above_ptr = input_data[inrow-1];
|
||||
below_ptr = input_data[inrow+2];
|
||||
|
||||
/* Special case for first column: pretend column -1 is same as column 0 */
|
||||
membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) +
|
||||
GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]);
|
||||
neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) +
|
||||
GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) +
|
||||
GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[2]) +
|
||||
GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[2]);
|
||||
neighsum += neighsum;
|
||||
neighsum += GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[2]) +
|
||||
GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[2]);
|
||||
membersum = membersum * memberscale + neighsum * neighscale;
|
||||
*outptr++ = (JSAMPLE) ((membersum + 32768) >> 16);
|
||||
inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2;
|
||||
|
||||
for (colctr = output_cols - 2; colctr > 0; colctr--) {
|
||||
/* sum of pixels directly mapped to this output element */
|
||||
membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) +
|
||||
GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]);
|
||||
/* sum of edge-neighbor pixels */
|
||||
neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) +
|
||||
GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) +
|
||||
GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[2]) +
|
||||
GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[2]);
|
||||
/* The edge-neighbors count twice as much as corner-neighbors */
|
||||
neighsum += neighsum;
|
||||
/* Add in the corner-neighbors */
|
||||
neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[2]) +
|
||||
GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[2]);
|
||||
/* form final output scaled up by 2^16 */
|
||||
membersum = membersum * memberscale + neighsum * neighscale;
|
||||
/* round, descale and output it */
|
||||
*outptr++ = (JSAMPLE) ((membersum + 32768) >> 16);
|
||||
inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2;
|
||||
}
|
||||
|
||||
/* Special case for last column */
|
||||
membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) +
|
||||
GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]);
|
||||
neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) +
|
||||
GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) +
|
||||
GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[1]) +
|
||||
GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[1]);
|
||||
neighsum += neighsum;
|
||||
neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[1]) +
|
||||
GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[1]);
|
||||
membersum = membersum * memberscale + neighsum * neighscale;
|
||||
*outptr = (JSAMPLE) ((membersum + 32768) >> 16);
|
||||
|
||||
inrow += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Downsample pixel values of a single component.
|
||||
* This version handles the special case of a full-size component,
|
||||
* with smoothing. One row of context is required.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY output_data)
|
||||
{
|
||||
int outrow;
|
||||
JDIMENSION colctr;
|
||||
JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
|
||||
register JSAMPROW inptr, above_ptr, below_ptr, outptr;
|
||||
INT32 membersum, neighsum, memberscale, neighscale;
|
||||
int colsum, lastcolsum, nextcolsum;
|
||||
|
||||
/* Expand input data enough to let all the output samples be generated
|
||||
* by the standard loop. Special-casing padded output would be more
|
||||
* efficient.
|
||||
*/
|
||||
expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2,
|
||||
cinfo->image_width, output_cols);
|
||||
|
||||
/* Each of the eight neighbor pixels contributes a fraction SF to the
|
||||
* smoothed pixel, while the main pixel contributes (1-8*SF). In order
|
||||
* to use integer arithmetic, these factors are multiplied by 2^16 = 65536.
|
||||
* Also recall that SF = smoothing_factor / 1024.
|
||||
*/
|
||||
|
||||
memberscale = 65536L - cinfo->smoothing_factor * 512L; /* scaled 1-8*SF */
|
||||
neighscale = cinfo->smoothing_factor * 64; /* scaled SF */
|
||||
|
||||
for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) {
|
||||
outptr = output_data[outrow];
|
||||
inptr = input_data[outrow];
|
||||
above_ptr = input_data[outrow-1];
|
||||
below_ptr = input_data[outrow+1];
|
||||
|
||||
/* Special case for first column */
|
||||
colsum = GETJSAMPLE(*above_ptr++) + GETJSAMPLE(*below_ptr++) +
|
||||
GETJSAMPLE(*inptr);
|
||||
membersum = GETJSAMPLE(*inptr++);
|
||||
nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) +
|
||||
GETJSAMPLE(*inptr);
|
||||
neighsum = colsum + (colsum - membersum) + nextcolsum;
|
||||
membersum = membersum * memberscale + neighsum * neighscale;
|
||||
*outptr++ = (JSAMPLE) ((membersum + 32768) >> 16);
|
||||
lastcolsum = colsum; colsum = nextcolsum;
|
||||
|
||||
for (colctr = output_cols - 2; colctr > 0; colctr--) {
|
||||
membersum = GETJSAMPLE(*inptr++);
|
||||
above_ptr++; below_ptr++;
|
||||
nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) +
|
||||
GETJSAMPLE(*inptr);
|
||||
neighsum = lastcolsum + (colsum - membersum) + nextcolsum;
|
||||
membersum = membersum * memberscale + neighsum * neighscale;
|
||||
*outptr++ = (JSAMPLE) ((membersum + 32768) >> 16);
|
||||
lastcolsum = colsum; colsum = nextcolsum;
|
||||
}
|
||||
|
||||
/* Special case for last column */
|
||||
membersum = GETJSAMPLE(*inptr);
|
||||
neighsum = lastcolsum + (colsum - membersum) + colsum;
|
||||
membersum = membersum * memberscale + neighsum * neighscale;
|
||||
*outptr = (JSAMPLE) ((membersum + 32768) >> 16);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* INPUT_SMOOTHING_SUPPORTED */
|
||||
|
||||
|
||||
/*
|
||||
* Module initialization routine for downsampling.
|
||||
* Note that we must select a routine for each component.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_downsampler (j_compress_ptr cinfo)
|
||||
{
|
||||
my_downsample_ptr downsample;
|
||||
int ci;
|
||||
jpeg_component_info * compptr;
|
||||
boolean smoothok = TRUE;
|
||||
|
||||
downsample = (my_downsample_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_downsampler));
|
||||
cinfo->downsample = (struct jpeg_downsampler *) downsample;
|
||||
downsample->pub.start_pass = start_pass_downsample;
|
||||
downsample->pub.downsample = sep_downsample;
|
||||
downsample->pub.need_context_rows = FALSE;
|
||||
|
||||
if (cinfo->CCIR601_sampling)
|
||||
ERREXIT(cinfo, JERR_CCIR601_NOTIMPL);
|
||||
|
||||
/* Verify we can handle the sampling factors, and set up method pointers */
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
if (compptr->h_samp_factor == cinfo->max_h_samp_factor &&
|
||||
compptr->v_samp_factor == cinfo->max_v_samp_factor) {
|
||||
#ifdef INPUT_SMOOTHING_SUPPORTED
|
||||
if (cinfo->smoothing_factor) {
|
||||
downsample->methods[ci] = fullsize_smooth_downsample;
|
||||
downsample->pub.need_context_rows = TRUE;
|
||||
} else
|
||||
#endif
|
||||
downsample->methods[ci] = fullsize_downsample;
|
||||
} else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor &&
|
||||
compptr->v_samp_factor == cinfo->max_v_samp_factor) {
|
||||
smoothok = FALSE;
|
||||
downsample->methods[ci] = h2v1_downsample;
|
||||
} else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor &&
|
||||
compptr->v_samp_factor * 2 == cinfo->max_v_samp_factor) {
|
||||
#ifdef INPUT_SMOOTHING_SUPPORTED
|
||||
if (cinfo->smoothing_factor) {
|
||||
downsample->methods[ci] = h2v2_smooth_downsample;
|
||||
downsample->pub.need_context_rows = TRUE;
|
||||
} else
|
||||
#endif
|
||||
downsample->methods[ci] = h2v2_downsample;
|
||||
} else if ((cinfo->max_h_samp_factor % compptr->h_samp_factor) == 0 &&
|
||||
(cinfo->max_v_samp_factor % compptr->v_samp_factor) == 0) {
|
||||
smoothok = FALSE;
|
||||
downsample->methods[ci] = int_downsample;
|
||||
} else
|
||||
ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL);
|
||||
}
|
||||
|
||||
#ifdef INPUT_SMOOTHING_SUPPORTED
|
||||
if (cinfo->smoothing_factor && !smoothok)
|
||||
TRACEMS(cinfo, 0, JTRC_SMOOTH_NOTIMPL);
|
||||
#endif
|
||||
}
|
||||
371
neo/renderer/jpeg-6/jctrans.c
Normal file
371
neo/renderer/jpeg-6/jctrans.c
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* jctrans.c
|
||||
*
|
||||
* Copyright (C) 1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains library routines for transcoding compression,
|
||||
* that is, writing raw DCT coefficient arrays to an output JPEG file.
|
||||
* The routines in jcapimin.c will also be needed by a transcoder.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
LOCAL void transencode_master_selection
|
||||
JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays));
|
||||
LOCAL void transencode_coef_controller
|
||||
JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays));
|
||||
|
||||
|
||||
/*
|
||||
* Compression initialization for writing raw-coefficient data.
|
||||
* Before calling this, all parameters and a data destination must be set up.
|
||||
* Call jpeg_finish_compress() to actually write the data.
|
||||
*
|
||||
* The number of passed virtual arrays must match cinfo->num_components.
|
||||
* Note that the virtual arrays need not be filled or even realized at
|
||||
* the time write_coefficients is called; indeed, if the virtual arrays
|
||||
* were requested from this compression object's memory manager, they
|
||||
* typically will be realized during this routine and filled afterwards.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)
|
||||
{
|
||||
if (cinfo->global_state != CSTATE_START)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
/* Mark all tables to be written */
|
||||
jpeg_suppress_tables(cinfo, FALSE);
|
||||
/* (Re)initialize error mgr and destination modules */
|
||||
(*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);
|
||||
(*cinfo->dest->init_destination) (cinfo);
|
||||
/* Perform master selection of active modules */
|
||||
transencode_master_selection(cinfo, coef_arrays);
|
||||
/* Wait for jpeg_finish_compress() call */
|
||||
cinfo->next_scanline = 0; /* so jpeg_write_marker works */
|
||||
cinfo->global_state = CSTATE_WRCOEFS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the compression object with default parameters,
|
||||
* then copy from the source object all parameters needed for lossless
|
||||
* transcoding. Parameters that can be varied without loss (such as
|
||||
* scan script and Huffman optimization) are left in their default states.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_copy_critical_parameters (j_decompress_ptr srcinfo,
|
||||
j_compress_ptr dstinfo)
|
||||
{
|
||||
JQUANT_TBL ** qtblptr;
|
||||
jpeg_component_info *incomp, *outcomp;
|
||||
JQUANT_TBL *c_quant, *slot_quant;
|
||||
int tblno, ci, coefi;
|
||||
|
||||
/* Safety check to ensure start_compress not called yet. */
|
||||
if (dstinfo->global_state != CSTATE_START)
|
||||
ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state);
|
||||
/* Copy fundamental image dimensions */
|
||||
dstinfo->image_width = srcinfo->image_width;
|
||||
dstinfo->image_height = srcinfo->image_height;
|
||||
dstinfo->input_components = srcinfo->num_components;
|
||||
dstinfo->in_color_space = srcinfo->jpeg_color_space;
|
||||
/* Initialize all parameters to default values */
|
||||
jpeg_set_defaults(dstinfo);
|
||||
/* jpeg_set_defaults may choose wrong colorspace, eg YCbCr if input is RGB.
|
||||
* Fix it to get the right header markers for the image colorspace.
|
||||
*/
|
||||
jpeg_set_colorspace(dstinfo, srcinfo->jpeg_color_space);
|
||||
dstinfo->data_precision = srcinfo->data_precision;
|
||||
dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling;
|
||||
/* Copy the source's quantization tables. */
|
||||
for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) {
|
||||
if (srcinfo->quant_tbl_ptrs[tblno] != NULL) {
|
||||
qtblptr = & dstinfo->quant_tbl_ptrs[tblno];
|
||||
if (*qtblptr == NULL)
|
||||
*qtblptr = jpeg_alloc_quant_table((j_common_ptr) dstinfo);
|
||||
MEMCOPY((*qtblptr)->quantval,
|
||||
srcinfo->quant_tbl_ptrs[tblno]->quantval,
|
||||
SIZEOF((*qtblptr)->quantval));
|
||||
(*qtblptr)->sent_table = FALSE;
|
||||
}
|
||||
}
|
||||
/* Copy the source's per-component info.
|
||||
* Note we assume jpeg_set_defaults has allocated the dest comp_info array.
|
||||
*/
|
||||
dstinfo->num_components = srcinfo->num_components;
|
||||
if (dstinfo->num_components < 1 || dstinfo->num_components > MAX_COMPONENTS)
|
||||
ERREXIT2(dstinfo, JERR_COMPONENT_COUNT, dstinfo->num_components,
|
||||
MAX_COMPONENTS);
|
||||
for (ci = 0, incomp = srcinfo->comp_info, outcomp = dstinfo->comp_info;
|
||||
ci < dstinfo->num_components; ci++, incomp++, outcomp++) {
|
||||
outcomp->component_id = incomp->component_id;
|
||||
outcomp->h_samp_factor = incomp->h_samp_factor;
|
||||
outcomp->v_samp_factor = incomp->v_samp_factor;
|
||||
outcomp->quant_tbl_no = incomp->quant_tbl_no;
|
||||
/* Make sure saved quantization table for component matches the qtable
|
||||
* slot. If not, the input file re-used this qtable slot.
|
||||
* IJG encoder currently cannot duplicate this.
|
||||
*/
|
||||
tblno = outcomp->quant_tbl_no;
|
||||
if (tblno < 0 || tblno >= NUM_QUANT_TBLS ||
|
||||
srcinfo->quant_tbl_ptrs[tblno] == NULL)
|
||||
ERREXIT1(dstinfo, JERR_NO_QUANT_TABLE, tblno);
|
||||
slot_quant = srcinfo->quant_tbl_ptrs[tblno];
|
||||
c_quant = incomp->quant_table;
|
||||
if (c_quant != NULL) {
|
||||
for (coefi = 0; coefi < DCTSIZE2; coefi++) {
|
||||
if (c_quant->quantval[coefi] != slot_quant->quantval[coefi])
|
||||
ERREXIT1(dstinfo, JERR_MISMATCHED_QUANT_TABLE, tblno);
|
||||
}
|
||||
}
|
||||
/* Note: we do not copy the source's Huffman table assignments;
|
||||
* instead we rely on jpeg_set_colorspace to have made a suitable choice.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Master selection of compression modules for transcoding.
|
||||
* This substitutes for jcinit.c's initialization of the full compressor.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
transencode_master_selection (j_compress_ptr cinfo,
|
||||
jvirt_barray_ptr * coef_arrays)
|
||||
{
|
||||
/* Although we don't actually use input_components for transcoding,
|
||||
* jcmaster.c's initial_setup will complain if input_components is 0.
|
||||
*/
|
||||
cinfo->input_components = 1;
|
||||
/* Initialize master control (includes parameter checking/processing) */
|
||||
jinit_c_master_control(cinfo, TRUE /* transcode only */);
|
||||
|
||||
/* Entropy encoding: either Huffman or arithmetic coding. */
|
||||
if (cinfo->arith_code) {
|
||||
ERREXIT(cinfo, JERR_ARITH_NOTIMPL);
|
||||
} else {
|
||||
if (cinfo->progressive_mode) {
|
||||
#ifdef C_PROGRESSIVE_SUPPORTED
|
||||
jinit_phuff_encoder(cinfo);
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
} else
|
||||
jinit_huff_encoder(cinfo);
|
||||
}
|
||||
|
||||
/* We need a special coefficient buffer controller. */
|
||||
transencode_coef_controller(cinfo, coef_arrays);
|
||||
|
||||
jinit_marker_writer(cinfo);
|
||||
|
||||
/* We can now tell the memory manager to allocate virtual arrays. */
|
||||
(*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo);
|
||||
|
||||
/* Write the datastream header (SOI) immediately.
|
||||
* Frame and scan headers are postponed till later.
|
||||
* This lets application insert special markers after the SOI.
|
||||
*/
|
||||
(*cinfo->marker->write_file_header) (cinfo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The rest of this file is a special implementation of the coefficient
|
||||
* buffer controller. This is similar to jccoefct.c, but it handles only
|
||||
* output from presupplied virtual arrays. Furthermore, we generate any
|
||||
* dummy padding blocks on-the-fly rather than expecting them to be present
|
||||
* in the arrays.
|
||||
*/
|
||||
|
||||
/* Private buffer controller object */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_c_coef_controller pub; /* public fields */
|
||||
|
||||
JDIMENSION iMCU_row_num; /* iMCU row # within image */
|
||||
JDIMENSION mcu_ctr; /* counts MCUs processed in current row */
|
||||
int MCU_vert_offset; /* counts MCU rows within iMCU row */
|
||||
int MCU_rows_per_iMCU_row; /* number of such rows needed */
|
||||
|
||||
/* Virtual block array for each component. */
|
||||
jvirt_barray_ptr * whole_image;
|
||||
|
||||
/* Workspace for constructing dummy blocks at right/bottom edges. */
|
||||
JBLOCKROW dummy_buffer[C_MAX_BLOCKS_IN_MCU];
|
||||
} my_coef_controller;
|
||||
|
||||
typedef my_coef_controller * my_coef_ptr;
|
||||
|
||||
|
||||
LOCAL void
|
||||
start_iMCU_row (j_compress_ptr cinfo)
|
||||
/* Reset within-iMCU-row counters for a new row */
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
|
||||
/* In an interleaved scan, an MCU row is the same as an iMCU row.
|
||||
* In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows.
|
||||
* But at the bottom of the image, process only what's left.
|
||||
*/
|
||||
if (cinfo->comps_in_scan > 1) {
|
||||
coef->MCU_rows_per_iMCU_row = 1;
|
||||
} else {
|
||||
if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1))
|
||||
coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor;
|
||||
else
|
||||
coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height;
|
||||
}
|
||||
|
||||
coef->mcu_ctr = 0;
|
||||
coef->MCU_vert_offset = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a processing pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode)
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
|
||||
if (pass_mode != JBUF_CRANK_DEST)
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
|
||||
coef->iMCU_row_num = 0;
|
||||
start_iMCU_row(cinfo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process some data.
|
||||
* We process the equivalent of one fully interleaved MCU row ("iMCU" row)
|
||||
* per call, ie, v_samp_factor block rows for each component in the scan.
|
||||
* The data is obtained from the virtual arrays and fed to the entropy coder.
|
||||
* Returns TRUE if the iMCU row is completed, FALSE if suspended.
|
||||
*
|
||||
* NB: input_buf is ignored; it is likely to be a NULL pointer.
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
JDIMENSION MCU_col_num; /* index of current MCU within row */
|
||||
JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1;
|
||||
JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
|
||||
int blkn, ci, xindex, yindex, yoffset, blockcnt;
|
||||
JDIMENSION start_col;
|
||||
JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN];
|
||||
JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU];
|
||||
JBLOCKROW buffer_ptr;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
/* Align the virtual buffers for the components used in this scan. */
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
buffer[ci] = (*cinfo->mem->access_virt_barray)
|
||||
((j_common_ptr) cinfo, coef->whole_image[compptr->component_index],
|
||||
coef->iMCU_row_num * compptr->v_samp_factor,
|
||||
(JDIMENSION) compptr->v_samp_factor, FALSE);
|
||||
}
|
||||
|
||||
/* Loop to process one whole iMCU row */
|
||||
for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row;
|
||||
yoffset++) {
|
||||
for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row;
|
||||
MCU_col_num++) {
|
||||
/* Construct list of pointers to DCT blocks belonging to this MCU */
|
||||
blkn = 0; /* index of current DCT block within MCU */
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
start_col = MCU_col_num * compptr->MCU_width;
|
||||
blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width
|
||||
: compptr->last_col_width;
|
||||
for (yindex = 0; yindex < compptr->MCU_height; yindex++) {
|
||||
if (coef->iMCU_row_num < last_iMCU_row ||
|
||||
yindex+yoffset < compptr->last_row_height) {
|
||||
/* Fill in pointers to real blocks in this row */
|
||||
buffer_ptr = buffer[ci][yindex+yoffset] + start_col;
|
||||
for (xindex = 0; xindex < blockcnt; xindex++)
|
||||
MCU_buffer[blkn++] = buffer_ptr++;
|
||||
} else {
|
||||
/* At bottom of image, need a whole row of dummy blocks */
|
||||
xindex = 0;
|
||||
}
|
||||
/* Fill in any dummy blocks needed in this row.
|
||||
* Dummy blocks are filled in the same way as in jccoefct.c:
|
||||
* all zeroes in the AC entries, DC entries equal to previous
|
||||
* block's DC value. The init routine has already zeroed the
|
||||
* AC entries, so we need only set the DC entries correctly.
|
||||
*/
|
||||
for (; xindex < compptr->MCU_width; xindex++) {
|
||||
MCU_buffer[blkn] = coef->dummy_buffer[blkn];
|
||||
MCU_buffer[blkn][0][0] = MCU_buffer[blkn-1][0][0];
|
||||
blkn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Try to write the MCU. */
|
||||
if (! (*cinfo->entropy->encode_mcu) (cinfo, MCU_buffer)) {
|
||||
/* Suspension forced; update state counters and exit */
|
||||
coef->MCU_vert_offset = yoffset;
|
||||
coef->mcu_ctr = MCU_col_num;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
/* Completed an MCU row, but perhaps not an iMCU row */
|
||||
coef->mcu_ctr = 0;
|
||||
}
|
||||
/* Completed the iMCU row, advance counters for next one */
|
||||
coef->iMCU_row_num++;
|
||||
start_iMCU_row(cinfo);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize coefficient buffer controller.
|
||||
*
|
||||
* Each passed coefficient array must be the right size for that
|
||||
* coefficient: width_in_blocks wide and height_in_blocks high,
|
||||
* with unitheight at least v_samp_factor.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
transencode_coef_controller (j_compress_ptr cinfo,
|
||||
jvirt_barray_ptr * coef_arrays)
|
||||
{
|
||||
my_coef_ptr coef;
|
||||
JBLOCKROW buffer;
|
||||
int i;
|
||||
|
||||
coef = (my_coef_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_coef_controller));
|
||||
cinfo->coef = (struct jpeg_c_coef_controller *) coef;
|
||||
coef->pub.start_pass = start_pass_coef;
|
||||
coef->pub.compress_data = compress_output;
|
||||
|
||||
/* Save pointer to virtual arrays */
|
||||
coef->whole_image = coef_arrays;
|
||||
|
||||
/* Allocate and pre-zero space for dummy DCT blocks. */
|
||||
buffer = (JBLOCKROW)
|
||||
(*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK));
|
||||
jzero_far((void FAR *) buffer, C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK));
|
||||
for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) {
|
||||
coef->dummy_buffer[i] = buffer + i;
|
||||
}
|
||||
}
|
||||
398
neo/renderer/jpeg-6/jdapimin.c
Normal file
398
neo/renderer/jpeg-6/jdapimin.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* jdapimin.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains application interface code for the decompression half
|
||||
* of the JPEG library. These are the "minimum" API routines that may be
|
||||
* needed in either the normal full-decompression case or the
|
||||
* transcoding-only case.
|
||||
*
|
||||
* Most of the routines intended to be called directly by an application
|
||||
* are in this file or in jdapistd.c. But also see jcomapi.c for routines
|
||||
* shared by compression and decompression, and jdtrans.c for the transcoding
|
||||
* case.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/*
|
||||
* Initialization of a JPEG decompression object.
|
||||
* The error manager must already be set up (in case memory manager fails).
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_create_decompress (j_decompress_ptr cinfo)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* For debugging purposes, zero the whole master structure.
|
||||
* But error manager pointer is already there, so save and restore it.
|
||||
*/
|
||||
{
|
||||
struct jpeg_error_mgr * err = cinfo->err;
|
||||
MEMZERO(cinfo, SIZEOF(struct jpeg_decompress_struct));
|
||||
cinfo->err = err;
|
||||
}
|
||||
cinfo->is_decompressor = TRUE;
|
||||
|
||||
/* Initialize a memory manager instance for this object */
|
||||
jinit_memory_mgr((j_common_ptr) cinfo);
|
||||
|
||||
/* Zero out pointers to permanent structures. */
|
||||
cinfo->progress = NULL;
|
||||
cinfo->src = NULL;
|
||||
|
||||
for (i = 0; i < NUM_QUANT_TBLS; i++)
|
||||
cinfo->quant_tbl_ptrs[i] = NULL;
|
||||
|
||||
for (i = 0; i < NUM_HUFF_TBLS; i++) {
|
||||
cinfo->dc_huff_tbl_ptrs[i] = NULL;
|
||||
cinfo->ac_huff_tbl_ptrs[i] = NULL;
|
||||
}
|
||||
|
||||
/* Initialize marker processor so application can override methods
|
||||
* for COM, APPn markers before calling jpeg_read_header.
|
||||
*/
|
||||
jinit_marker_reader(cinfo);
|
||||
|
||||
/* And initialize the overall input controller. */
|
||||
jinit_input_controller(cinfo);
|
||||
|
||||
/* OK, I'm ready */
|
||||
cinfo->global_state = DSTATE_START;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Destruction of a JPEG decompression object
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_destroy_decompress (j_decompress_ptr cinfo)
|
||||
{
|
||||
jpeg_destroy((j_common_ptr) cinfo); /* use common routine */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Abort processing of a JPEG decompression operation,
|
||||
* but don't destroy the object itself.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_abort_decompress (j_decompress_ptr cinfo)
|
||||
{
|
||||
jpeg_abort((j_common_ptr) cinfo); /* use common routine */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Install a special processing method for COM or APPn markers.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_set_marker_processor (j_decompress_ptr cinfo, int marker_code,
|
||||
jpeg_marker_parser_method routine)
|
||||
{
|
||||
if (marker_code == JPEG_COM)
|
||||
cinfo->marker->process_COM = routine;
|
||||
else if (marker_code >= JPEG_APP0 && marker_code <= JPEG_APP0+15)
|
||||
cinfo->marker->process_APPn[marker_code-JPEG_APP0] = routine;
|
||||
else
|
||||
ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set default decompression parameters.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
default_decompress_parms (j_decompress_ptr cinfo)
|
||||
{
|
||||
/* Guess the input colorspace, and set output colorspace accordingly. */
|
||||
/* (Wish JPEG committee had provided a real way to specify this...) */
|
||||
/* Note application may override our guesses. */
|
||||
switch (cinfo->num_components) {
|
||||
case 1:
|
||||
cinfo->jpeg_color_space = JCS_GRAYSCALE;
|
||||
cinfo->out_color_space = JCS_GRAYSCALE;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (cinfo->saw_JFIF_marker) {
|
||||
cinfo->jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */
|
||||
} else if (cinfo->saw_Adobe_marker) {
|
||||
switch (cinfo->Adobe_transform) {
|
||||
case 0:
|
||||
cinfo->jpeg_color_space = JCS_RGB;
|
||||
break;
|
||||
case 1:
|
||||
cinfo->jpeg_color_space = JCS_YCbCr;
|
||||
break;
|
||||
default:
|
||||
WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform);
|
||||
cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Saw no special markers, try to guess from the component IDs */
|
||||
int cid0 = cinfo->comp_info[0].component_id;
|
||||
int cid1 = cinfo->comp_info[1].component_id;
|
||||
int cid2 = cinfo->comp_info[2].component_id;
|
||||
|
||||
if (cid0 == 1 && cid1 == 2 && cid2 == 3)
|
||||
cinfo->jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */
|
||||
else if (cid0 == 82 && cid1 == 71 && cid2 == 66)
|
||||
cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */
|
||||
else {
|
||||
TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2);
|
||||
cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */
|
||||
}
|
||||
}
|
||||
/* Always guess RGB is proper output colorspace. */
|
||||
cinfo->out_color_space = JCS_RGB;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (cinfo->saw_Adobe_marker) {
|
||||
switch (cinfo->Adobe_transform) {
|
||||
case 0:
|
||||
cinfo->jpeg_color_space = JCS_CMYK;
|
||||
break;
|
||||
case 2:
|
||||
cinfo->jpeg_color_space = JCS_YCCK;
|
||||
break;
|
||||
default:
|
||||
WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform);
|
||||
cinfo->jpeg_color_space = JCS_YCCK; /* assume it's YCCK */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* No special markers, assume straight CMYK. */
|
||||
cinfo->jpeg_color_space = JCS_CMYK;
|
||||
}
|
||||
cinfo->out_color_space = JCS_CMYK;
|
||||
break;
|
||||
|
||||
default:
|
||||
cinfo->jpeg_color_space = JCS_UNKNOWN;
|
||||
cinfo->out_color_space = JCS_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set defaults for other decompression parameters. */
|
||||
cinfo->scale_num = 1; /* 1:1 scaling */
|
||||
cinfo->scale_denom = 1;
|
||||
cinfo->output_gamma = 1.0;
|
||||
cinfo->buffered_image = FALSE;
|
||||
cinfo->raw_data_out = FALSE;
|
||||
cinfo->dct_method = JDCT_DEFAULT;
|
||||
cinfo->do_fancy_upsampling = TRUE;
|
||||
cinfo->do_block_smoothing = TRUE;
|
||||
cinfo->quantize_colors = FALSE;
|
||||
/* We set these in case application only sets quantize_colors. */
|
||||
cinfo->dither_mode = JDITHER_FS;
|
||||
#ifdef QUANT_2PASS_SUPPORTED
|
||||
cinfo->two_pass_quantize = TRUE;
|
||||
#else
|
||||
cinfo->two_pass_quantize = FALSE;
|
||||
#endif
|
||||
cinfo->desired_number_of_colors = 256;
|
||||
cinfo->colormap = NULL;
|
||||
/* Initialize for no mode change in buffered-image mode. */
|
||||
cinfo->enable_1pass_quant = FALSE;
|
||||
cinfo->enable_external_quant = FALSE;
|
||||
cinfo->enable_2pass_quant = FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Decompression startup: read start of JPEG datastream to see what's there.
|
||||
* Need only initialize JPEG object and supply a data source before calling.
|
||||
*
|
||||
* This routine will read as far as the first SOS marker (ie, actual start of
|
||||
* compressed data), and will save all tables and parameters in the JPEG
|
||||
* object. It will also initialize the decompression parameters to default
|
||||
* values, and finally return JPEG_HEADER_OK. On return, the application may
|
||||
* adjust the decompression parameters and then call jpeg_start_decompress.
|
||||
* (Or, if the application only wanted to determine the image parameters,
|
||||
* the data need not be decompressed. In that case, call jpeg_abort or
|
||||
* jpeg_destroy to release any temporary space.)
|
||||
* If an abbreviated (tables only) datastream is presented, the routine will
|
||||
* return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then
|
||||
* re-use the JPEG object to read the abbreviated image datastream(s).
|
||||
* It is unnecessary (but OK) to call jpeg_abort in this case.
|
||||
* The JPEG_SUSPENDED return code only occurs if the data source module
|
||||
* requests suspension of the decompressor. In this case the application
|
||||
* should load more source data and then re-call jpeg_read_header to resume
|
||||
* processing.
|
||||
* If a non-suspending data source is used and require_image is TRUE, then the
|
||||
* return code need not be inspected since only JPEG_HEADER_OK is possible.
|
||||
*
|
||||
* This routine is now just a front end to jpeg_consume_input, with some
|
||||
* extra error checking.
|
||||
*/
|
||||
|
||||
GLOBAL int
|
||||
jpeg_read_header (j_decompress_ptr cinfo, boolean require_image)
|
||||
{
|
||||
int retcode;
|
||||
|
||||
if (cinfo->global_state != DSTATE_START &&
|
||||
cinfo->global_state != DSTATE_INHEADER)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
|
||||
retcode = jpeg_consume_input(cinfo);
|
||||
|
||||
switch (retcode) {
|
||||
case JPEG_REACHED_SOS:
|
||||
retcode = JPEG_HEADER_OK;
|
||||
break;
|
||||
case JPEG_REACHED_EOI:
|
||||
if (require_image) /* Complain if application wanted an image */
|
||||
ERREXIT(cinfo, JERR_NO_IMAGE);
|
||||
/* Reset to start state; it would be safer to require the application to
|
||||
* call jpeg_abort, but we can't change it now for compatibility reasons.
|
||||
* A side effect is to free any temporary memory (there shouldn't be any).
|
||||
*/
|
||||
jpeg_abort((j_common_ptr) cinfo); /* sets state = DSTATE_START */
|
||||
retcode = JPEG_HEADER_TABLES_ONLY;
|
||||
break;
|
||||
case JPEG_SUSPENDED:
|
||||
/* no work */
|
||||
break;
|
||||
}
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Consume data in advance of what the decompressor requires.
|
||||
* This can be called at any time once the decompressor object has
|
||||
* been created and a data source has been set up.
|
||||
*
|
||||
* This routine is essentially a state machine that handles a couple
|
||||
* of critical state-transition actions, namely initial setup and
|
||||
* transition from header scanning to ready-for-start_decompress.
|
||||
* All the actual input is done via the input controller's consume_input
|
||||
* method.
|
||||
*/
|
||||
|
||||
GLOBAL int
|
||||
jpeg_consume_input (j_decompress_ptr cinfo)
|
||||
{
|
||||
int retcode = JPEG_SUSPENDED;
|
||||
|
||||
/* NB: every possible DSTATE value should be listed in this switch */
|
||||
switch (cinfo->global_state) {
|
||||
case DSTATE_START:
|
||||
/* Start-of-datastream actions: reset appropriate modules */
|
||||
(*cinfo->inputctl->reset_input_controller) (cinfo);
|
||||
/* Initialize application's data source module */
|
||||
(*cinfo->src->init_source) (cinfo);
|
||||
cinfo->global_state = DSTATE_INHEADER;
|
||||
/*FALLTHROUGH*/
|
||||
case DSTATE_INHEADER:
|
||||
retcode = (*cinfo->inputctl->consume_input) (cinfo);
|
||||
if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */
|
||||
/* Set up default parameters based on header data */
|
||||
default_decompress_parms(cinfo);
|
||||
/* Set global state: ready for start_decompress */
|
||||
cinfo->global_state = DSTATE_READY;
|
||||
}
|
||||
break;
|
||||
case DSTATE_READY:
|
||||
/* Can't advance past first SOS until start_decompress is called */
|
||||
retcode = JPEG_REACHED_SOS;
|
||||
break;
|
||||
case DSTATE_PRELOAD:
|
||||
case DSTATE_PRESCAN:
|
||||
case DSTATE_SCANNING:
|
||||
case DSTATE_RAW_OK:
|
||||
case DSTATE_BUFIMAGE:
|
||||
case DSTATE_BUFPOST:
|
||||
case DSTATE_STOPPING:
|
||||
retcode = (*cinfo->inputctl->consume_input) (cinfo);
|
||||
break;
|
||||
default:
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
}
|
||||
return retcode;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Have we finished reading the input file?
|
||||
*/
|
||||
|
||||
GLOBAL boolean
|
||||
jpeg_input_complete (j_decompress_ptr cinfo)
|
||||
{
|
||||
/* Check for valid jpeg object */
|
||||
if (cinfo->global_state < DSTATE_START ||
|
||||
cinfo->global_state > DSTATE_STOPPING)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
return cinfo->inputctl->eoi_reached;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Is there more than one scan?
|
||||
*/
|
||||
|
||||
GLOBAL boolean
|
||||
jpeg_has_multiple_scans (j_decompress_ptr cinfo)
|
||||
{
|
||||
/* Only valid after jpeg_read_header completes */
|
||||
if (cinfo->global_state < DSTATE_READY ||
|
||||
cinfo->global_state > DSTATE_STOPPING)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
return cinfo->inputctl->has_multiple_scans;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish JPEG decompression.
|
||||
*
|
||||
* This will normally just verify the file trailer and release temp storage.
|
||||
*
|
||||
* Returns FALSE if suspended. The return value need be inspected only if
|
||||
* a suspending data source is used.
|
||||
*/
|
||||
|
||||
GLOBAL boolean
|
||||
jpeg_finish_decompress (j_decompress_ptr cinfo)
|
||||
{
|
||||
if ((cinfo->global_state == DSTATE_SCANNING ||
|
||||
cinfo->global_state == DSTATE_RAW_OK) && ! cinfo->buffered_image) {
|
||||
/* Terminate final pass of non-buffered mode */
|
||||
if (cinfo->output_scanline < cinfo->output_height)
|
||||
ERREXIT(cinfo, JERR_TOO_LITTLE_DATA);
|
||||
(*cinfo->master->finish_output_pass) (cinfo);
|
||||
cinfo->global_state = DSTATE_STOPPING;
|
||||
} else if (cinfo->global_state == DSTATE_BUFIMAGE) {
|
||||
/* Finishing after a buffered-image operation */
|
||||
cinfo->global_state = DSTATE_STOPPING;
|
||||
} else if (cinfo->global_state != DSTATE_STOPPING) {
|
||||
/* STOPPING = repeat call after a suspension, anything else is error */
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
}
|
||||
/* Read until EOI */
|
||||
while (! cinfo->inputctl->eoi_reached) {
|
||||
if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED)
|
||||
return FALSE; /* Suspend, come back later */
|
||||
}
|
||||
/* Do final cleanup */
|
||||
(*cinfo->src->term_source) (cinfo);
|
||||
/* We can use jpeg_abort to release memory and reset global_state */
|
||||
jpeg_abort((j_common_ptr) cinfo);
|
||||
return TRUE;
|
||||
}
|
||||
275
neo/renderer/jpeg-6/jdapistd.c
Normal file
275
neo/renderer/jpeg-6/jdapistd.c
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* jdapistd.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains application interface code for the decompression half
|
||||
* of the JPEG library. These are the "standard" API routines that are
|
||||
* used in the normal full-decompression case. They are not used by a
|
||||
* transcoding-only application. Note that if an application links in
|
||||
* jpeg_start_decompress, it will end up linking in the entire decompressor.
|
||||
* We thus must separate this file from jdapimin.c to avoid linking the
|
||||
* whole decompression library into a transcoder.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
LOCAL boolean output_pass_setup JPP((j_decompress_ptr cinfo));
|
||||
|
||||
|
||||
/*
|
||||
* Decompression initialization.
|
||||
* jpeg_read_header must be completed before calling this.
|
||||
*
|
||||
* If a multipass operating mode was selected, this will do all but the
|
||||
* last pass, and thus may take a great deal of time.
|
||||
*
|
||||
* Returns FALSE if suspended. The return value need be inspected only if
|
||||
* a suspending data source is used.
|
||||
*/
|
||||
|
||||
GLOBAL boolean
|
||||
jpeg_start_decompress (j_decompress_ptr cinfo)
|
||||
{
|
||||
if (cinfo->global_state == DSTATE_READY) {
|
||||
/* First call: initialize master control, select active modules */
|
||||
jinit_master_decompress(cinfo);
|
||||
if (cinfo->buffered_image) {
|
||||
/* No more work here; expecting jpeg_start_output next */
|
||||
cinfo->global_state = DSTATE_BUFIMAGE;
|
||||
return TRUE;
|
||||
}
|
||||
cinfo->global_state = DSTATE_PRELOAD;
|
||||
}
|
||||
if (cinfo->global_state == DSTATE_PRELOAD) {
|
||||
/* If file has multiple scans, absorb them all into the coef buffer */
|
||||
if (cinfo->inputctl->has_multiple_scans) {
|
||||
#ifdef D_MULTISCAN_FILES_SUPPORTED
|
||||
for (;;) {
|
||||
int retcode;
|
||||
/* Call progress monitor hook if present */
|
||||
if (cinfo->progress != NULL)
|
||||
(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
|
||||
/* Absorb some more input */
|
||||
retcode = (*cinfo->inputctl->consume_input) (cinfo);
|
||||
if (retcode == JPEG_SUSPENDED)
|
||||
return FALSE;
|
||||
if (retcode == JPEG_REACHED_EOI)
|
||||
break;
|
||||
/* Advance progress counter if appropriate */
|
||||
if (cinfo->progress != NULL &&
|
||||
(retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) {
|
||||
if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) {
|
||||
/* jdmaster underestimated number of scans; ratchet up one scan */
|
||||
cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif /* D_MULTISCAN_FILES_SUPPORTED */
|
||||
}
|
||||
cinfo->output_scan_number = cinfo->input_scan_number;
|
||||
} else if (cinfo->global_state != DSTATE_PRESCAN)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
/* Perform any dummy output passes, and set up for the final pass */
|
||||
return output_pass_setup(cinfo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set up for an output pass, and perform any dummy pass(es) needed.
|
||||
* Common subroutine for jpeg_start_decompress and jpeg_start_output.
|
||||
* Entry: global_state = DSTATE_PRESCAN only if previously suspended.
|
||||
* Exit: If done, returns TRUE and sets global_state for proper output mode.
|
||||
* If suspended, returns FALSE and sets global_state = DSTATE_PRESCAN.
|
||||
*/
|
||||
|
||||
LOCAL boolean
|
||||
output_pass_setup (j_decompress_ptr cinfo)
|
||||
{
|
||||
if (cinfo->global_state != DSTATE_PRESCAN) {
|
||||
/* First call: do pass setup */
|
||||
(*cinfo->master->prepare_for_output_pass) (cinfo);
|
||||
cinfo->output_scanline = 0;
|
||||
cinfo->global_state = DSTATE_PRESCAN;
|
||||
}
|
||||
/* Loop over any required dummy passes */
|
||||
while (cinfo->master->is_dummy_pass) {
|
||||
#ifdef QUANT_2PASS_SUPPORTED
|
||||
/* Crank through the dummy pass */
|
||||
while (cinfo->output_scanline < cinfo->output_height) {
|
||||
JDIMENSION last_scanline;
|
||||
/* Call progress monitor hook if present */
|
||||
if (cinfo->progress != NULL) {
|
||||
cinfo->progress->pass_counter = (long) cinfo->output_scanline;
|
||||
cinfo->progress->pass_limit = (long) cinfo->output_height;
|
||||
(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
|
||||
}
|
||||
/* Process some data */
|
||||
last_scanline = cinfo->output_scanline;
|
||||
(*cinfo->main->process_data) (cinfo, (JSAMPARRAY) NULL,
|
||||
&cinfo->output_scanline, (JDIMENSION) 0);
|
||||
if (cinfo->output_scanline == last_scanline)
|
||||
return FALSE; /* No progress made, must suspend */
|
||||
}
|
||||
/* Finish up dummy pass, and set up for another one */
|
||||
(*cinfo->master->finish_output_pass) (cinfo);
|
||||
(*cinfo->master->prepare_for_output_pass) (cinfo);
|
||||
cinfo->output_scanline = 0;
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif /* QUANT_2PASS_SUPPORTED */
|
||||
}
|
||||
/* Ready for application to drive output pass through
|
||||
* jpeg_read_scanlines or jpeg_read_raw_data.
|
||||
*/
|
||||
cinfo->global_state = cinfo->raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read some scanlines of data from the JPEG decompressor.
|
||||
*
|
||||
* The return value will be the number of lines actually read.
|
||||
* This may be less than the number requested in several cases,
|
||||
* including bottom of image, data source suspension, and operating
|
||||
* modes that emit multiple scanlines at a time.
|
||||
*
|
||||
* Note: we warn about excess calls to jpeg_read_scanlines() since
|
||||
* this likely signals an application programmer error. However,
|
||||
* an oversize buffer (max_lines > scanlines remaining) is not an error.
|
||||
*/
|
||||
|
||||
GLOBAL JDIMENSION
|
||||
jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines,
|
||||
JDIMENSION max_lines)
|
||||
{
|
||||
JDIMENSION row_ctr;
|
||||
|
||||
if (cinfo->global_state != DSTATE_SCANNING)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
if (cinfo->output_scanline >= cinfo->output_height) {
|
||||
WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call progress monitor hook if present */
|
||||
if (cinfo->progress != NULL) {
|
||||
cinfo->progress->pass_counter = (long) cinfo->output_scanline;
|
||||
cinfo->progress->pass_limit = (long) cinfo->output_height;
|
||||
(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
|
||||
}
|
||||
|
||||
/* Process some data */
|
||||
row_ctr = 0;
|
||||
(*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines);
|
||||
cinfo->output_scanline += row_ctr;
|
||||
return row_ctr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Alternate entry point to read raw data.
|
||||
* Processes exactly one iMCU row per call, unless suspended.
|
||||
*/
|
||||
|
||||
GLOBAL JDIMENSION
|
||||
jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data,
|
||||
JDIMENSION max_lines)
|
||||
{
|
||||
JDIMENSION lines_per_iMCU_row;
|
||||
|
||||
if (cinfo->global_state != DSTATE_RAW_OK)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
if (cinfo->output_scanline >= cinfo->output_height) {
|
||||
WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call progress monitor hook if present */
|
||||
if (cinfo->progress != NULL) {
|
||||
cinfo->progress->pass_counter = (long) cinfo->output_scanline;
|
||||
cinfo->progress->pass_limit = (long) cinfo->output_height;
|
||||
(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
|
||||
}
|
||||
|
||||
/* Verify that at least one iMCU row can be returned. */
|
||||
lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size;
|
||||
if (max_lines < lines_per_iMCU_row)
|
||||
ERREXIT(cinfo, JERR_BUFFER_SIZE);
|
||||
|
||||
/* Decompress directly into user's buffer. */
|
||||
if (! (*cinfo->coef->decompress_data) (cinfo, data))
|
||||
return 0; /* suspension forced, can do nothing more */
|
||||
|
||||
/* OK, we processed one iMCU row. */
|
||||
cinfo->output_scanline += lines_per_iMCU_row;
|
||||
return lines_per_iMCU_row;
|
||||
}
|
||||
|
||||
|
||||
/* Additional entry points for buffered-image mode. */
|
||||
|
||||
#ifdef D_MULTISCAN_FILES_SUPPORTED
|
||||
|
||||
/*
|
||||
* Initialize for an output pass in buffered-image mode.
|
||||
*/
|
||||
|
||||
GLOBAL boolean
|
||||
jpeg_start_output (j_decompress_ptr cinfo, int scan_number)
|
||||
{
|
||||
if (cinfo->global_state != DSTATE_BUFIMAGE &&
|
||||
cinfo->global_state != DSTATE_PRESCAN)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
/* Limit scan number to valid range */
|
||||
if (scan_number <= 0)
|
||||
scan_number = 1;
|
||||
if (cinfo->inputctl->eoi_reached &&
|
||||
scan_number > cinfo->input_scan_number)
|
||||
scan_number = cinfo->input_scan_number;
|
||||
cinfo->output_scan_number = scan_number;
|
||||
/* Perform any dummy output passes, and set up for the real pass */
|
||||
return output_pass_setup(cinfo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish up after an output pass in buffered-image mode.
|
||||
*
|
||||
* Returns FALSE if suspended. The return value need be inspected only if
|
||||
* a suspending data source is used.
|
||||
*/
|
||||
|
||||
GLOBAL boolean
|
||||
jpeg_finish_output (j_decompress_ptr cinfo)
|
||||
{
|
||||
if ((cinfo->global_state == DSTATE_SCANNING ||
|
||||
cinfo->global_state == DSTATE_RAW_OK) && cinfo->buffered_image) {
|
||||
/* Terminate this pass. */
|
||||
/* We do not require the whole pass to have been completed. */
|
||||
(*cinfo->master->finish_output_pass) (cinfo);
|
||||
cinfo->global_state = DSTATE_BUFPOST;
|
||||
} else if (cinfo->global_state != DSTATE_BUFPOST) {
|
||||
/* BUFPOST = repeat call after a suspension, anything else is error */
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
}
|
||||
/* Read markers looking for SOS or EOI */
|
||||
while (cinfo->input_scan_number <= cinfo->output_scan_number &&
|
||||
! cinfo->inputctl->eoi_reached) {
|
||||
if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED)
|
||||
return FALSE; /* Suspend, come back later */
|
||||
}
|
||||
cinfo->global_state = DSTATE_BUFIMAGE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif /* D_MULTISCAN_FILES_SUPPORTED */
|
||||
151
neo/renderer/jpeg-6/jdatadst.c
Normal file
151
neo/renderer/jpeg-6/jdatadst.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* jdatadst.c
|
||||
*
|
||||
* Copyright (C) 1994, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains compression data destination routines for the case of
|
||||
* emitting JPEG data to a file (or any stdio stream). While these routines
|
||||
* are sufficient for most applications, some will want to use a different
|
||||
* destination manager.
|
||||
* IMPORTANT: we assume that fwrite() will correctly transcribe an array of
|
||||
* JOCTETs into 8-bit-wide elements on external storage. If char is wider
|
||||
* than 8 bits on your machine, you may need to do some tweaking.
|
||||
*/
|
||||
|
||||
/* this is not a core library module, so it doesn't define JPEG_INTERNALS */
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jerror.h"
|
||||
|
||||
|
||||
/* Expanded data destination object for stdio output */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_destination_mgr pub; /* public fields */
|
||||
|
||||
FILE * outfile; /* target stream */
|
||||
JOCTET * buffer; /* start of buffer */
|
||||
} my_destination_mgr;
|
||||
|
||||
typedef my_destination_mgr * my_dest_ptr;
|
||||
|
||||
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
|
||||
|
||||
|
||||
/*
|
||||
* Initialize destination --- called by jpeg_start_compress
|
||||
* before any data is actually written.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
init_destination (j_compress_ptr cinfo)
|
||||
{
|
||||
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
|
||||
|
||||
/* Allocate the output buffer --- it will be released when done with image */
|
||||
dest->buffer = (JOCTET *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
|
||||
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Empty the output buffer --- called whenever buffer fills up.
|
||||
*
|
||||
* In typical applications, this should write the entire output buffer
|
||||
* (ignoring the current state of next_output_byte & free_in_buffer),
|
||||
* reset the pointer & count to the start of the buffer, and return TRUE
|
||||
* indicating that the buffer has been dumped.
|
||||
*
|
||||
* In applications that need to be able to suspend compression due to output
|
||||
* overrun, a FALSE return indicates that the buffer cannot be emptied now.
|
||||
* In this situation, the compressor will return to its caller (possibly with
|
||||
* an indication that it has not accepted all the supplied scanlines). The
|
||||
* application should resume compression after it has made more room in the
|
||||
* output buffer. Note that there are substantial restrictions on the use of
|
||||
* suspension --- see the documentation.
|
||||
*
|
||||
* When suspending, the compressor will back up to a convenient restart point
|
||||
* (typically the start of the current MCU). next_output_byte & free_in_buffer
|
||||
* indicate where the restart point will be if the current call returns FALSE.
|
||||
* Data beyond this point will be regenerated after resumption, so do not
|
||||
* write it out when emptying the buffer externally.
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
empty_output_buffer (j_compress_ptr cinfo)
|
||||
{
|
||||
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
|
||||
|
||||
if (JFWRITE(dest->outfile, dest->buffer, OUTPUT_BUF_SIZE) !=
|
||||
(size_t) OUTPUT_BUF_SIZE)
|
||||
ERREXIT(cinfo, JERR_FILE_WRITE);
|
||||
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Terminate destination --- called by jpeg_finish_compress
|
||||
* after all data has been written. Usually needs to flush buffer.
|
||||
*
|
||||
* NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
|
||||
* application must deal with any cleanup that should happen even
|
||||
* for error exit.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
term_destination (j_compress_ptr cinfo)
|
||||
{
|
||||
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
|
||||
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
||||
|
||||
/* Write any data remaining in the buffer */
|
||||
if (datacount > 0) {
|
||||
if (JFWRITE(dest->outfile, dest->buffer, datacount) != datacount)
|
||||
ERREXIT(cinfo, JERR_FILE_WRITE);
|
||||
}
|
||||
fflush(dest->outfile);
|
||||
/* Make sure we wrote the output file OK */
|
||||
if (ferror(dest->outfile))
|
||||
ERREXIT(cinfo, JERR_FILE_WRITE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Prepare for output to a stdio stream.
|
||||
* The caller must have already opened the stream, and is responsible
|
||||
* for closing it after finishing compression.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_stdio_dest (j_compress_ptr cinfo, FILE * outfile)
|
||||
{
|
||||
my_dest_ptr dest;
|
||||
|
||||
/* The destination object is made permanent so that multiple JPEG images
|
||||
* can be written to the same file without re-executing jpeg_stdio_dest.
|
||||
* This makes it dangerous to use this manager and a different destination
|
||||
* manager serially with the same JPEG object, because their private object
|
||||
* sizes may be different. Caveat programmer.
|
||||
*/
|
||||
if (cinfo->dest == NULL) { /* first time for this JPEG object? */
|
||||
cinfo->dest = (struct jpeg_destination_mgr *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
|
||||
SIZEOF(my_destination_mgr));
|
||||
}
|
||||
|
||||
dest = (my_dest_ptr) cinfo->dest;
|
||||
dest->pub.init_destination = init_destination;
|
||||
dest->pub.empty_output_buffer = empty_output_buffer;
|
||||
dest->pub.term_destination = term_destination;
|
||||
dest->outfile = outfile;
|
||||
}
|
||||
204
neo/renderer/jpeg-6/jdatasrc.c
Normal file
204
neo/renderer/jpeg-6/jdatasrc.c
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* jdatasrc.c
|
||||
*
|
||||
* Copyright (C) 1994, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains decompression data source routines for the case of
|
||||
* reading JPEG data from a file (or any stdio stream). While these routines
|
||||
* are sufficient for most applications, some will want to use a different
|
||||
* source manager.
|
||||
* IMPORTANT: we assume that fread() will correctly transcribe an array of
|
||||
* JOCTETs from 8-bit-wide elements on external storage. If char is wider
|
||||
* than 8 bits on your machine, you may need to do some tweaking.
|
||||
*/
|
||||
|
||||
|
||||
/* this is not a core library module, so it doesn't define JPEG_INTERNALS */
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jerror.h"
|
||||
|
||||
|
||||
/* Expanded data source object for stdio input */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_source_mgr pub; /* public fields */
|
||||
|
||||
unsigned char *infile; /* source stream */
|
||||
JOCTET * buffer; /* start of buffer */
|
||||
boolean start_of_file; /* have we gotten any data yet? */
|
||||
} my_source_mgr;
|
||||
|
||||
typedef my_source_mgr * my_src_ptr;
|
||||
|
||||
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
|
||||
|
||||
|
||||
/*
|
||||
* Initialize source --- called by jpeg_read_header
|
||||
* before any data is actually read.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
init_source (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_src_ptr src = (my_src_ptr) cinfo->src;
|
||||
|
||||
/* We reset the empty-input-file flag for each image,
|
||||
* but we don't clear the input buffer.
|
||||
* This is correct behavior for reading a series of images from one source.
|
||||
*/
|
||||
src->start_of_file = TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fill the input buffer --- called whenever buffer is emptied.
|
||||
*
|
||||
* In typical applications, this should read fresh data into the buffer
|
||||
* (ignoring the current state of next_input_byte & bytes_in_buffer),
|
||||
* reset the pointer & count to the start of the buffer, and return TRUE
|
||||
* indicating that the buffer has been reloaded. It is not necessary to
|
||||
* fill the buffer entirely, only to obtain at least one more byte.
|
||||
*
|
||||
* There is no such thing as an EOF return. If the end of the file has been
|
||||
* reached, the routine has a choice of ERREXIT() or inserting fake data into
|
||||
* the buffer. In most cases, generating a warning message and inserting a
|
||||
* fake EOI marker is the best course of action --- this will allow the
|
||||
* decompressor to output however much of the image is there. However,
|
||||
* the resulting error message is misleading if the real problem is an empty
|
||||
* input file, so we handle that case specially.
|
||||
*
|
||||
* In applications that need to be able to suspend compression due to input
|
||||
* not being available yet, a FALSE return indicates that no more data can be
|
||||
* obtained right now, but more may be forthcoming later. In this situation,
|
||||
* the decompressor will return to its caller (with an indication of the
|
||||
* number of scanlines it has read, if any). The application should resume
|
||||
* decompression after it has loaded more data into the input buffer. Note
|
||||
* that there are substantial restrictions on the use of suspension --- see
|
||||
* the documentation.
|
||||
*
|
||||
* When suspending, the decompressor will back up to a convenient restart point
|
||||
* (typically the start of the current MCU). next_input_byte & bytes_in_buffer
|
||||
* indicate where the restart point will be if the current call returns FALSE.
|
||||
* Data beyond this point must be rescanned after resumption, so move it to
|
||||
* the front of the buffer rather than discarding it.
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
fill_input_buffer (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_src_ptr src = (my_src_ptr) cinfo->src;
|
||||
|
||||
memcpy( src->buffer, src->infile, INPUT_BUF_SIZE );
|
||||
|
||||
src->infile += INPUT_BUF_SIZE;
|
||||
|
||||
src->pub.next_input_byte = src->buffer;
|
||||
src->pub.bytes_in_buffer = INPUT_BUF_SIZE;
|
||||
src->start_of_file = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Skip data --- used to skip over a potentially large amount of
|
||||
* uninteresting data (such as an APPn marker).
|
||||
*
|
||||
* Writers of suspendable-input applications must note that skip_input_data
|
||||
* is not granted the right to give a suspension return. If the skip extends
|
||||
* beyond the data currently in the buffer, the buffer can be marked empty so
|
||||
* that the next read will cause a fill_input_buffer call that can suspend.
|
||||
* Arranging for additional bytes to be discarded before reloading the input
|
||||
* buffer is the application writer's problem.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
||||
{
|
||||
my_src_ptr src = (my_src_ptr) cinfo->src;
|
||||
|
||||
/* Just a dumb implementation for now. Could use fseek() except
|
||||
* it doesn't work on pipes. Not clear that being smart is worth
|
||||
* any trouble anyway --- large skips are infrequent.
|
||||
*/
|
||||
if (num_bytes > 0) {
|
||||
while (num_bytes > (long) src->pub.bytes_in_buffer) {
|
||||
num_bytes -= (long) src->pub.bytes_in_buffer;
|
||||
(void) fill_input_buffer(cinfo);
|
||||
/* note we assume that fill_input_buffer will never return FALSE,
|
||||
* so suspension need not be handled.
|
||||
*/
|
||||
}
|
||||
src->pub.next_input_byte += (size_t) num_bytes;
|
||||
src->pub.bytes_in_buffer -= (size_t) num_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* An additional method that can be provided by data source modules is the
|
||||
* resync_to_restart method for error recovery in the presence of RST markers.
|
||||
* For the moment, this source module just uses the default resync method
|
||||
* provided by the JPEG library. That method assumes that no backtracking
|
||||
* is possible.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Terminate source --- called by jpeg_finish_decompress
|
||||
* after all data has been read. Often a no-op.
|
||||
*
|
||||
* NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
|
||||
* application must deal with any cleanup that should happen even
|
||||
* for error exit.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
term_source (j_decompress_ptr cinfo)
|
||||
{
|
||||
/* no work necessary here */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Prepare for input from a stdio stream.
|
||||
* The caller must have already opened the stream, and is responsible
|
||||
* for closing it after finishing decompression.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_stdio_src (j_decompress_ptr cinfo, unsigned char *infile)
|
||||
{
|
||||
my_src_ptr src;
|
||||
|
||||
/* The source object and input buffer are made permanent so that a series
|
||||
* of JPEG images can be read from the same file by calling jpeg_stdio_src
|
||||
* only before the first one. (If we discarded the buffer at the end of
|
||||
* one image, we'd likely lose the start of the next one.)
|
||||
* This makes it unsafe to use this manager and a different source
|
||||
* manager serially with the same JPEG object. Caveat programmer.
|
||||
*/
|
||||
if (cinfo->src == NULL) { /* first time for this JPEG object? */
|
||||
cinfo->src = (struct jpeg_source_mgr *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
|
||||
SIZEOF(my_source_mgr));
|
||||
src = (my_src_ptr) cinfo->src;
|
||||
src->buffer = (JOCTET *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
|
||||
INPUT_BUF_SIZE * SIZEOF(JOCTET));
|
||||
}
|
||||
|
||||
src = (my_src_ptr) cinfo->src;
|
||||
src->pub.init_source = init_source;
|
||||
src->pub.fill_input_buffer = fill_input_buffer;
|
||||
src->pub.skip_input_data = skip_input_data;
|
||||
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
|
||||
src->pub.term_source = term_source;
|
||||
src->infile = infile;
|
||||
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
|
||||
src->pub.next_input_byte = NULL; /* until buffer loaded */
|
||||
}
|
||||
725
neo/renderer/jpeg-6/jdcoefct.c
Normal file
725
neo/renderer/jpeg-6/jdcoefct.c
Normal file
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
* jdcoefct.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains the coefficient buffer controller for decompression.
|
||||
* This controller is the top level of the JPEG decompressor proper.
|
||||
* The coefficient buffer lies between entropy decoding and inverse-DCT steps.
|
||||
*
|
||||
* In buffered-image mode, this controller is the interface between
|
||||
* input-oriented processing and output-oriented processing.
|
||||
* Also, the input side (only) is used when reading a file for transcoding.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
/* Block smoothing is only applicable for progressive JPEG, so: */
|
||||
#ifndef D_PROGRESSIVE_SUPPORTED
|
||||
#undef BLOCK_SMOOTHING_SUPPORTED
|
||||
#endif
|
||||
|
||||
/* Private buffer controller object */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_d_coef_controller pub; /* public fields */
|
||||
|
||||
/* These variables keep track of the current location of the input side. */
|
||||
/* cinfo->input_iMCU_row is also used for this. */
|
||||
JDIMENSION MCU_ctr; /* counts MCUs processed in current row */
|
||||
int MCU_vert_offset; /* counts MCU rows within iMCU row */
|
||||
int MCU_rows_per_iMCU_row; /* number of such rows needed */
|
||||
|
||||
/* The output side's location is represented by cinfo->output_iMCU_row. */
|
||||
|
||||
/* In single-pass modes, it's sufficient to buffer just one MCU.
|
||||
* We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks,
|
||||
* and let the entropy decoder write into that workspace each time.
|
||||
* (On 80x86, the workspace is FAR even though it's not really very big;
|
||||
* this is to keep the module interfaces unchanged when a large coefficient
|
||||
* buffer is necessary.)
|
||||
* In multi-pass modes, this array points to the current MCU's blocks
|
||||
* within the virtual arrays; it is used only by the input side.
|
||||
*/
|
||||
JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU];
|
||||
|
||||
#ifdef D_MULTISCAN_FILES_SUPPORTED
|
||||
/* In multi-pass modes, we need a virtual block array for each component. */
|
||||
jvirt_barray_ptr whole_image[MAX_COMPONENTS];
|
||||
#endif
|
||||
|
||||
#ifdef BLOCK_SMOOTHING_SUPPORTED
|
||||
/* When doing block smoothing, we latch coefficient Al values here */
|
||||
int * coef_bits_latch;
|
||||
#define SAVED_COEFS 6 /* we save coef_bits[0..5] */
|
||||
#endif
|
||||
} my_coef_controller;
|
||||
|
||||
typedef my_coef_controller * my_coef_ptr;
|
||||
|
||||
/* Forward declarations */
|
||||
METHODDEF int decompress_onepass
|
||||
JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf));
|
||||
#ifdef D_MULTISCAN_FILES_SUPPORTED
|
||||
METHODDEF int decompress_data
|
||||
JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf));
|
||||
#endif
|
||||
#ifdef BLOCK_SMOOTHING_SUPPORTED
|
||||
LOCAL boolean smoothing_ok JPP((j_decompress_ptr cinfo));
|
||||
METHODDEF int decompress_smooth_data
|
||||
JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf));
|
||||
#endif
|
||||
|
||||
|
||||
LOCAL void
|
||||
start_iMCU_row (j_decompress_ptr cinfo)
|
||||
/* Reset within-iMCU-row counters for a new row (input side) */
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
|
||||
/* In an interleaved scan, an MCU row is the same as an iMCU row.
|
||||
* In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows.
|
||||
* But at the bottom of the image, process only what's left.
|
||||
*/
|
||||
if (cinfo->comps_in_scan > 1) {
|
||||
coef->MCU_rows_per_iMCU_row = 1;
|
||||
} else {
|
||||
if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1))
|
||||
coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor;
|
||||
else
|
||||
coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height;
|
||||
}
|
||||
|
||||
coef->MCU_ctr = 0;
|
||||
coef->MCU_vert_offset = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for an input processing pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_input_pass (j_decompress_ptr cinfo)
|
||||
{
|
||||
cinfo->input_iMCU_row = 0;
|
||||
start_iMCU_row(cinfo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for an output processing pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_output_pass (j_decompress_ptr cinfo)
|
||||
{
|
||||
#ifdef BLOCK_SMOOTHING_SUPPORTED
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
|
||||
/* If multipass, check to see whether to use block smoothing on this pass */
|
||||
if (coef->pub.coef_arrays != NULL) {
|
||||
if (cinfo->do_block_smoothing && smoothing_ok(cinfo))
|
||||
coef->pub.decompress_data = decompress_smooth_data;
|
||||
else
|
||||
coef->pub.decompress_data = decompress_data;
|
||||
}
|
||||
#endif
|
||||
cinfo->output_iMCU_row = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Decompress and return some data in the single-pass case.
|
||||
* Always attempts to emit one fully interleaved MCU row ("iMCU" row).
|
||||
* Input and output must run in lockstep since we have only a one-MCU buffer.
|
||||
* Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED.
|
||||
*
|
||||
* NB: output_buf contains a plane for each component in image.
|
||||
* For single pass, this is the same as the components in the scan.
|
||||
*/
|
||||
|
||||
METHODDEF int
|
||||
decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf)
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
JDIMENSION MCU_col_num; /* index of current MCU within row */
|
||||
JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1;
|
||||
JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
|
||||
int blkn, ci, xindex, yindex, yoffset, useful_width;
|
||||
JSAMPARRAY output_ptr;
|
||||
JDIMENSION start_col, output_col;
|
||||
jpeg_component_info *compptr;
|
||||
inverse_DCT_method_ptr inverse_DCT;
|
||||
|
||||
/* Loop to process as much as one whole iMCU row */
|
||||
for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row;
|
||||
yoffset++) {
|
||||
for (MCU_col_num = coef->MCU_ctr; MCU_col_num <= last_MCU_col;
|
||||
MCU_col_num++) {
|
||||
/* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */
|
||||
jzero_far((void FAR *) coef->MCU_buffer[0],
|
||||
(size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK)));
|
||||
if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) {
|
||||
/* Suspension forced; update state counters and exit */
|
||||
coef->MCU_vert_offset = yoffset;
|
||||
coef->MCU_ctr = MCU_col_num;
|
||||
return JPEG_SUSPENDED;
|
||||
}
|
||||
/* Determine where data should go in output_buf and do the IDCT thing.
|
||||
* We skip dummy blocks at the right and bottom edges (but blkn gets
|
||||
* incremented past them!). Note the inner loop relies on having
|
||||
* allocated the MCU_buffer[] blocks sequentially.
|
||||
*/
|
||||
blkn = 0; /* index of current DCT block within MCU */
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
/* Don't bother to IDCT an uninteresting component. */
|
||||
if (! compptr->component_needed) {
|
||||
blkn += compptr->MCU_blocks;
|
||||
continue;
|
||||
}
|
||||
inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index];
|
||||
useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width
|
||||
: compptr->last_col_width;
|
||||
output_ptr = output_buf[ci] + yoffset * compptr->DCT_scaled_size;
|
||||
start_col = MCU_col_num * compptr->MCU_sample_width;
|
||||
for (yindex = 0; yindex < compptr->MCU_height; yindex++) {
|
||||
if (cinfo->input_iMCU_row < last_iMCU_row ||
|
||||
yoffset+yindex < compptr->last_row_height) {
|
||||
output_col = start_col;
|
||||
for (xindex = 0; xindex < useful_width; xindex++) {
|
||||
(*inverse_DCT) (cinfo, compptr,
|
||||
(JCOEFPTR) coef->MCU_buffer[blkn+xindex],
|
||||
output_ptr, output_col);
|
||||
output_col += compptr->DCT_scaled_size;
|
||||
}
|
||||
}
|
||||
blkn += compptr->MCU_width;
|
||||
output_ptr += compptr->DCT_scaled_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Completed an MCU row, but perhaps not an iMCU row */
|
||||
coef->MCU_ctr = 0;
|
||||
}
|
||||
/* Completed the iMCU row, advance counters for next one */
|
||||
cinfo->output_iMCU_row++;
|
||||
if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) {
|
||||
start_iMCU_row(cinfo);
|
||||
return JPEG_ROW_COMPLETED;
|
||||
}
|
||||
/* Completed the scan */
|
||||
(*cinfo->inputctl->finish_input_pass) (cinfo);
|
||||
return JPEG_SCAN_COMPLETED;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dummy consume-input routine for single-pass operation.
|
||||
*/
|
||||
|
||||
METHODDEF int
|
||||
dummy_consume_data (j_decompress_ptr cinfo)
|
||||
{
|
||||
return JPEG_SUSPENDED; /* Always indicate nothing was done */
|
||||
}
|
||||
|
||||
|
||||
#ifdef D_MULTISCAN_FILES_SUPPORTED
|
||||
|
||||
/*
|
||||
* Consume input data and store it in the full-image coefficient buffer.
|
||||
* We read as much as one fully interleaved MCU row ("iMCU" row) per call,
|
||||
* ie, v_samp_factor block rows for each component in the scan.
|
||||
* Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED.
|
||||
*/
|
||||
|
||||
METHODDEF int
|
||||
consume_data (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
JDIMENSION MCU_col_num; /* index of current MCU within row */
|
||||
int blkn, ci, xindex, yindex, yoffset;
|
||||
JDIMENSION start_col;
|
||||
JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN];
|
||||
JBLOCKROW buffer_ptr;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
/* Align the virtual buffers for the components used in this scan. */
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
buffer[ci] = (*cinfo->mem->access_virt_barray)
|
||||
((j_common_ptr) cinfo, coef->whole_image[compptr->component_index],
|
||||
cinfo->input_iMCU_row * compptr->v_samp_factor,
|
||||
(JDIMENSION) compptr->v_samp_factor, TRUE);
|
||||
/* Note: entropy decoder expects buffer to be zeroed,
|
||||
* but this is handled automatically by the memory manager
|
||||
* because we requested a pre-zeroed array.
|
||||
*/
|
||||
}
|
||||
|
||||
/* Loop to process one whole iMCU row */
|
||||
for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row;
|
||||
yoffset++) {
|
||||
for (MCU_col_num = coef->MCU_ctr; MCU_col_num < cinfo->MCUs_per_row;
|
||||
MCU_col_num++) {
|
||||
/* Construct list of pointers to DCT blocks belonging to this MCU */
|
||||
blkn = 0; /* index of current DCT block within MCU */
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
start_col = MCU_col_num * compptr->MCU_width;
|
||||
for (yindex = 0; yindex < compptr->MCU_height; yindex++) {
|
||||
buffer_ptr = buffer[ci][yindex+yoffset] + start_col;
|
||||
for (xindex = 0; xindex < compptr->MCU_width; xindex++) {
|
||||
coef->MCU_buffer[blkn++] = buffer_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Try to fetch the MCU. */
|
||||
if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) {
|
||||
/* Suspension forced; update state counters and exit */
|
||||
coef->MCU_vert_offset = yoffset;
|
||||
coef->MCU_ctr = MCU_col_num;
|
||||
return JPEG_SUSPENDED;
|
||||
}
|
||||
}
|
||||
/* Completed an MCU row, but perhaps not an iMCU row */
|
||||
coef->MCU_ctr = 0;
|
||||
}
|
||||
/* Completed the iMCU row, advance counters for next one */
|
||||
if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) {
|
||||
start_iMCU_row(cinfo);
|
||||
return JPEG_ROW_COMPLETED;
|
||||
}
|
||||
/* Completed the scan */
|
||||
(*cinfo->inputctl->finish_input_pass) (cinfo);
|
||||
return JPEG_SCAN_COMPLETED;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Decompress and return some data in the multi-pass case.
|
||||
* Always attempts to emit one fully interleaved MCU row ("iMCU" row).
|
||||
* Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED.
|
||||
*
|
||||
* NB: output_buf contains a plane for each component in image.
|
||||
*/
|
||||
|
||||
METHODDEF int
|
||||
decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf)
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
|
||||
JDIMENSION block_num;
|
||||
int ci, block_row, block_rows;
|
||||
JBLOCKARRAY buffer;
|
||||
JBLOCKROW buffer_ptr;
|
||||
JSAMPARRAY output_ptr;
|
||||
JDIMENSION output_col;
|
||||
jpeg_component_info *compptr;
|
||||
inverse_DCT_method_ptr inverse_DCT;
|
||||
|
||||
/* Force some input to be done if we are getting ahead of the input. */
|
||||
while (cinfo->input_scan_number < cinfo->output_scan_number ||
|
||||
(cinfo->input_scan_number == cinfo->output_scan_number &&
|
||||
cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) {
|
||||
if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED)
|
||||
return JPEG_SUSPENDED;
|
||||
}
|
||||
|
||||
/* OK, output from the virtual arrays. */
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* Don't bother to IDCT an uninteresting component. */
|
||||
if (! compptr->component_needed)
|
||||
continue;
|
||||
/* Align the virtual buffer for this component. */
|
||||
buffer = (*cinfo->mem->access_virt_barray)
|
||||
((j_common_ptr) cinfo, coef->whole_image[ci],
|
||||
cinfo->output_iMCU_row * compptr->v_samp_factor,
|
||||
(JDIMENSION) compptr->v_samp_factor, FALSE);
|
||||
/* Count non-dummy DCT block rows in this iMCU row. */
|
||||
if (cinfo->output_iMCU_row < last_iMCU_row)
|
||||
block_rows = compptr->v_samp_factor;
|
||||
else {
|
||||
/* NB: can't use last_row_height here; it is input-side-dependent! */
|
||||
block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor);
|
||||
if (block_rows == 0) block_rows = compptr->v_samp_factor;
|
||||
}
|
||||
inverse_DCT = cinfo->idct->inverse_DCT[ci];
|
||||
output_ptr = output_buf[ci];
|
||||
/* Loop over all DCT blocks to be processed. */
|
||||
for (block_row = 0; block_row < block_rows; block_row++) {
|
||||
buffer_ptr = buffer[block_row];
|
||||
output_col = 0;
|
||||
for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) {
|
||||
(*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr,
|
||||
output_ptr, output_col);
|
||||
buffer_ptr++;
|
||||
output_col += compptr->DCT_scaled_size;
|
||||
}
|
||||
output_ptr += compptr->DCT_scaled_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows)
|
||||
return JPEG_ROW_COMPLETED;
|
||||
return JPEG_SCAN_COMPLETED;
|
||||
}
|
||||
|
||||
#endif /* D_MULTISCAN_FILES_SUPPORTED */
|
||||
|
||||
|
||||
#ifdef BLOCK_SMOOTHING_SUPPORTED
|
||||
|
||||
/*
|
||||
* This code applies interblock smoothing as described by section K.8
|
||||
* of the JPEG standard: the first 5 AC coefficients are estimated from
|
||||
* the DC values of a DCT block and its 8 neighboring blocks.
|
||||
* We apply smoothing only for progressive JPEG decoding, and only if
|
||||
* the coefficients it can estimate are not yet known to full precision.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Determine whether block smoothing is applicable and safe.
|
||||
* We also latch the current states of the coef_bits[] entries for the
|
||||
* AC coefficients; otherwise, if the input side of the decompressor
|
||||
* advances into a new scan, we might think the coefficients are known
|
||||
* more accurately than they really are.
|
||||
*/
|
||||
|
||||
LOCAL boolean
|
||||
smoothing_ok (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
boolean smoothing_useful = FALSE;
|
||||
int ci, coefi;
|
||||
jpeg_component_info *compptr;
|
||||
JQUANT_TBL * qtable;
|
||||
int * coef_bits;
|
||||
int * coef_bits_latch;
|
||||
|
||||
if (! cinfo->progressive_mode || cinfo->coef_bits == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* Allocate latch area if not already done */
|
||||
if (coef->coef_bits_latch == NULL)
|
||||
coef->coef_bits_latch = (int *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
cinfo->num_components *
|
||||
(SAVED_COEFS * SIZEOF(int)));
|
||||
coef_bits_latch = coef->coef_bits_latch;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* All components' quantization values must already be latched. */
|
||||
if ((qtable = compptr->quant_table) == NULL)
|
||||
return FALSE;
|
||||
/* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */
|
||||
for (coefi = 0; coefi <= 5; coefi++) {
|
||||
if (qtable->quantval[coefi] == 0)
|
||||
return FALSE;
|
||||
}
|
||||
/* DC values must be at least partly known for all components. */
|
||||
coef_bits = cinfo->coef_bits[ci];
|
||||
if (coef_bits[0] < 0)
|
||||
return FALSE;
|
||||
/* Block smoothing is helpful if some AC coefficients remain inaccurate. */
|
||||
for (coefi = 1; coefi <= 5; coefi++) {
|
||||
coef_bits_latch[coefi] = coef_bits[coefi];
|
||||
if (coef_bits[coefi] != 0)
|
||||
smoothing_useful = TRUE;
|
||||
}
|
||||
coef_bits_latch += SAVED_COEFS;
|
||||
}
|
||||
|
||||
return smoothing_useful;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Variant of decompress_data for use when doing block smoothing.
|
||||
*/
|
||||
|
||||
METHODDEF int
|
||||
decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf)
|
||||
{
|
||||
my_coef_ptr coef = (my_coef_ptr) cinfo->coef;
|
||||
JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1;
|
||||
JDIMENSION block_num, last_block_column;
|
||||
int ci, block_row, block_rows, access_rows;
|
||||
JBLOCKARRAY buffer;
|
||||
JBLOCKROW buffer_ptr, prev_block_row, next_block_row;
|
||||
JSAMPARRAY output_ptr;
|
||||
JDIMENSION output_col;
|
||||
jpeg_component_info *compptr;
|
||||
inverse_DCT_method_ptr inverse_DCT;
|
||||
boolean first_row, last_row;
|
||||
JBLOCK workspace;
|
||||
int *coef_bits;
|
||||
JQUANT_TBL *quanttbl;
|
||||
INT32 Q00,Q01,Q02,Q10,Q11,Q20, num;
|
||||
int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9;
|
||||
int Al, pred;
|
||||
|
||||
/* Force some input to be done if we are getting ahead of the input. */
|
||||
while (cinfo->input_scan_number <= cinfo->output_scan_number &&
|
||||
! cinfo->inputctl->eoi_reached) {
|
||||
if (cinfo->input_scan_number == cinfo->output_scan_number) {
|
||||
/* If input is working on current scan, we ordinarily want it to
|
||||
* have completed the current row. But if input scan is DC,
|
||||
* we want it to keep one row ahead so that next block row's DC
|
||||
* values are up to date.
|
||||
*/
|
||||
JDIMENSION delta = (cinfo->Ss == 0) ? 1 : 0;
|
||||
if (cinfo->input_iMCU_row > cinfo->output_iMCU_row+delta)
|
||||
break;
|
||||
}
|
||||
if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED)
|
||||
return JPEG_SUSPENDED;
|
||||
}
|
||||
|
||||
/* OK, output from the virtual arrays. */
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* Don't bother to IDCT an uninteresting component. */
|
||||
if (! compptr->component_needed)
|
||||
continue;
|
||||
/* Count non-dummy DCT block rows in this iMCU row. */
|
||||
if (cinfo->output_iMCU_row < last_iMCU_row) {
|
||||
block_rows = compptr->v_samp_factor;
|
||||
access_rows = block_rows * 2; /* this and next iMCU row */
|
||||
last_row = FALSE;
|
||||
} else {
|
||||
/* NB: can't use last_row_height here; it is input-side-dependent! */
|
||||
block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor);
|
||||
if (block_rows == 0) block_rows = compptr->v_samp_factor;
|
||||
access_rows = block_rows; /* this iMCU row only */
|
||||
last_row = TRUE;
|
||||
}
|
||||
/* Align the virtual buffer for this component. */
|
||||
if (cinfo->output_iMCU_row > 0) {
|
||||
access_rows += compptr->v_samp_factor; /* prior iMCU row too */
|
||||
buffer = (*cinfo->mem->access_virt_barray)
|
||||
((j_common_ptr) cinfo, coef->whole_image[ci],
|
||||
(cinfo->output_iMCU_row - 1) * compptr->v_samp_factor,
|
||||
(JDIMENSION) access_rows, FALSE);
|
||||
buffer += compptr->v_samp_factor; /* point to current iMCU row */
|
||||
first_row = FALSE;
|
||||
} else {
|
||||
buffer = (*cinfo->mem->access_virt_barray)
|
||||
((j_common_ptr) cinfo, coef->whole_image[ci],
|
||||
(JDIMENSION) 0, (JDIMENSION) access_rows, FALSE);
|
||||
first_row = TRUE;
|
||||
}
|
||||
/* Fetch component-dependent info */
|
||||
coef_bits = coef->coef_bits_latch + (ci * SAVED_COEFS);
|
||||
quanttbl = compptr->quant_table;
|
||||
Q00 = quanttbl->quantval[0];
|
||||
Q01 = quanttbl->quantval[1];
|
||||
Q10 = quanttbl->quantval[2];
|
||||
Q20 = quanttbl->quantval[3];
|
||||
Q11 = quanttbl->quantval[4];
|
||||
Q02 = quanttbl->quantval[5];
|
||||
inverse_DCT = cinfo->idct->inverse_DCT[ci];
|
||||
output_ptr = output_buf[ci];
|
||||
/* Loop over all DCT blocks to be processed. */
|
||||
for (block_row = 0; block_row < block_rows; block_row++) {
|
||||
buffer_ptr = buffer[block_row];
|
||||
if (first_row && block_row == 0)
|
||||
prev_block_row = buffer_ptr;
|
||||
else
|
||||
prev_block_row = buffer[block_row-1];
|
||||
if (last_row && block_row == block_rows-1)
|
||||
next_block_row = buffer_ptr;
|
||||
else
|
||||
next_block_row = buffer[block_row+1];
|
||||
/* We fetch the surrounding DC values using a sliding-register approach.
|
||||
* Initialize all nine here so as to do the right thing on narrow pics.
|
||||
*/
|
||||
DC1 = DC2 = DC3 = (int) prev_block_row[0][0];
|
||||
DC4 = DC5 = DC6 = (int) buffer_ptr[0][0];
|
||||
DC7 = DC8 = DC9 = (int) next_block_row[0][0];
|
||||
output_col = 0;
|
||||
last_block_column = compptr->width_in_blocks - 1;
|
||||
for (block_num = 0; block_num <= last_block_column; block_num++) {
|
||||
/* Fetch current DCT block into workspace so we can modify it. */
|
||||
jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1);
|
||||
/* Update DC values */
|
||||
if (block_num < last_block_column) {
|
||||
DC3 = (int) prev_block_row[1][0];
|
||||
DC6 = (int) buffer_ptr[1][0];
|
||||
DC9 = (int) next_block_row[1][0];
|
||||
}
|
||||
/* Compute coefficient estimates per K.8.
|
||||
* An estimate is applied only if coefficient is still zero,
|
||||
* and is not known to be fully accurate.
|
||||
*/
|
||||
/* AC01 */
|
||||
if ((Al=coef_bits[1]) != 0 && workspace[1] == 0) {
|
||||
num = 36 * Q00 * (DC4 - DC6);
|
||||
if (num >= 0) {
|
||||
pred = (int) (((Q01<<7) + num) / (Q01<<8));
|
||||
if (Al > 0 && pred >= (1<<Al))
|
||||
pred = (1<<Al)-1;
|
||||
} else {
|
||||
pred = (int) (((Q01<<7) - num) / (Q01<<8));
|
||||
if (Al > 0 && pred >= (1<<Al))
|
||||
pred = (1<<Al)-1;
|
||||
pred = -pred;
|
||||
}
|
||||
workspace[1] = (JCOEF) pred;
|
||||
}
|
||||
/* AC10 */
|
||||
if ((Al=coef_bits[2]) != 0 && workspace[8] == 0) {
|
||||
num = 36 * Q00 * (DC2 - DC8);
|
||||
if (num >= 0) {
|
||||
pred = (int) (((Q10<<7) + num) / (Q10<<8));
|
||||
if (Al > 0 && pred >= (1<<Al))
|
||||
pred = (1<<Al)-1;
|
||||
} else {
|
||||
pred = (int) (((Q10<<7) - num) / (Q10<<8));
|
||||
if (Al > 0 && pred >= (1<<Al))
|
||||
pred = (1<<Al)-1;
|
||||
pred = -pred;
|
||||
}
|
||||
workspace[8] = (JCOEF) pred;
|
||||
}
|
||||
/* AC20 */
|
||||
if ((Al=coef_bits[3]) != 0 && workspace[16] == 0) {
|
||||
num = 9 * Q00 * (DC2 + DC8 - 2*DC5);
|
||||
if (num >= 0) {
|
||||
pred = (int) (((Q20<<7) + num) / (Q20<<8));
|
||||
if (Al > 0 && pred >= (1<<Al))
|
||||
pred = (1<<Al)-1;
|
||||
} else {
|
||||
pred = (int) (((Q20<<7) - num) / (Q20<<8));
|
||||
if (Al > 0 && pred >= (1<<Al))
|
||||
pred = (1<<Al)-1;
|
||||
pred = -pred;
|
||||
}
|
||||
workspace[16] = (JCOEF) pred;
|
||||
}
|
||||
/* AC11 */
|
||||
if ((Al=coef_bits[4]) != 0 && workspace[9] == 0) {
|
||||
num = 5 * Q00 * (DC1 - DC3 - DC7 + DC9);
|
||||
if (num >= 0) {
|
||||
pred = (int) (((Q11<<7) + num) / (Q11<<8));
|
||||
if (Al > 0 && pred >= (1<<Al))
|
||||
pred = (1<<Al)-1;
|
||||
} else {
|
||||
pred = (int) (((Q11<<7) - num) / (Q11<<8));
|
||||
if (Al > 0 && pred >= (1<<Al))
|
||||
pred = (1<<Al)-1;
|
||||
pred = -pred;
|
||||
}
|
||||
workspace[9] = (JCOEF) pred;
|
||||
}
|
||||
/* AC02 */
|
||||
if ((Al=coef_bits[5]) != 0 && workspace[2] == 0) {
|
||||
num = 9 * Q00 * (DC4 + DC6 - 2*DC5);
|
||||
if (num >= 0) {
|
||||
pred = (int) (((Q02<<7) + num) / (Q02<<8));
|
||||
if (Al > 0 && pred >= (1<<Al))
|
||||
pred = (1<<Al)-1;
|
||||
} else {
|
||||
pred = (int) (((Q02<<7) - num) / (Q02<<8));
|
||||
if (Al > 0 && pred >= (1<<Al))
|
||||
pred = (1<<Al)-1;
|
||||
pred = -pred;
|
||||
}
|
||||
workspace[2] = (JCOEF) pred;
|
||||
}
|
||||
/* OK, do the IDCT */
|
||||
(*inverse_DCT) (cinfo, compptr, (JCOEFPTR) workspace,
|
||||
output_ptr, output_col);
|
||||
/* Advance for next column */
|
||||
DC1 = DC2; DC2 = DC3;
|
||||
DC4 = DC5; DC5 = DC6;
|
||||
DC7 = DC8; DC8 = DC9;
|
||||
buffer_ptr++, prev_block_row++, next_block_row++;
|
||||
output_col += compptr->DCT_scaled_size;
|
||||
}
|
||||
output_ptr += compptr->DCT_scaled_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows)
|
||||
return JPEG_ROW_COMPLETED;
|
||||
return JPEG_SCAN_COMPLETED;
|
||||
}
|
||||
|
||||
#endif /* BLOCK_SMOOTHING_SUPPORTED */
|
||||
|
||||
|
||||
/*
|
||||
* Initialize coefficient buffer controller.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer)
|
||||
{
|
||||
my_coef_ptr coef;
|
||||
|
||||
coef = (my_coef_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_coef_controller));
|
||||
cinfo->coef = (struct jpeg_d_coef_controller *) coef;
|
||||
coef->pub.start_input_pass = start_input_pass;
|
||||
coef->pub.start_output_pass = start_output_pass;
|
||||
#ifdef BLOCK_SMOOTHING_SUPPORTED
|
||||
coef->coef_bits_latch = NULL;
|
||||
#endif
|
||||
|
||||
/* Create the coefficient buffer. */
|
||||
if (need_full_buffer) {
|
||||
#ifdef D_MULTISCAN_FILES_SUPPORTED
|
||||
/* Allocate a full-image virtual array for each component, */
|
||||
/* padded to a multiple of samp_factor DCT blocks in each direction. */
|
||||
/* Note we ask for a pre-zeroed array. */
|
||||
int ci, access_rows;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
access_rows = compptr->v_samp_factor;
|
||||
#ifdef BLOCK_SMOOTHING_SUPPORTED
|
||||
/* If block smoothing could be used, need a bigger window */
|
||||
if (cinfo->progressive_mode)
|
||||
access_rows *= 3;
|
||||
#endif
|
||||
coef->whole_image[ci] = (*cinfo->mem->request_virt_barray)
|
||||
((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE,
|
||||
(JDIMENSION) jround_up((long) compptr->width_in_blocks,
|
||||
(long) compptr->h_samp_factor),
|
||||
(JDIMENSION) jround_up((long) compptr->height_in_blocks,
|
||||
(long) compptr->v_samp_factor),
|
||||
(JDIMENSION) access_rows);
|
||||
}
|
||||
coef->pub.consume_data = consume_data;
|
||||
coef->pub.decompress_data = decompress_data;
|
||||
coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
} else {
|
||||
/* We only need a single-MCU buffer. */
|
||||
JBLOCKROW buffer;
|
||||
int i;
|
||||
|
||||
buffer = (JBLOCKROW)
|
||||
(*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK));
|
||||
for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) {
|
||||
coef->MCU_buffer[i] = buffer + i;
|
||||
}
|
||||
coef->pub.consume_data = dummy_consume_data;
|
||||
coef->pub.decompress_data = decompress_onepass;
|
||||
coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */
|
||||
}
|
||||
}
|
||||
367
neo/renderer/jpeg-6/jdcolor.c
Normal file
367
neo/renderer/jpeg-6/jdcolor.c
Normal file
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* jdcolor.c
|
||||
*
|
||||
* Copyright (C) 1991-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains output colorspace conversion routines.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Private subobject */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_color_deconverter pub; /* public fields */
|
||||
|
||||
/* Private state for YCC->RGB conversion */
|
||||
int * Cr_r_tab; /* => table for Cr to R conversion */
|
||||
int * Cb_b_tab; /* => table for Cb to B conversion */
|
||||
INT32 * Cr_g_tab; /* => table for Cr to G conversion */
|
||||
INT32 * Cb_g_tab; /* => table for Cb to G conversion */
|
||||
} my_color_deconverter;
|
||||
|
||||
typedef my_color_deconverter * my_cconvert_ptr;
|
||||
|
||||
|
||||
/**************** YCbCr -> RGB conversion: most common case **************/
|
||||
|
||||
/*
|
||||
* YCbCr is defined per CCIR 601-1, except that Cb and Cr are
|
||||
* normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
|
||||
* The conversion equations to be implemented are therefore
|
||||
* R = Y + 1.40200 * Cr
|
||||
* G = Y - 0.34414 * Cb - 0.71414 * Cr
|
||||
* B = Y + 1.77200 * Cb
|
||||
* where Cb and Cr represent the incoming values less CENTERJSAMPLE.
|
||||
* (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.)
|
||||
*
|
||||
* To avoid floating-point arithmetic, we represent the fractional constants
|
||||
* as integers scaled up by 2^16 (about 4 digits precision); we have to divide
|
||||
* the products by 2^16, with appropriate rounding, to get the correct answer.
|
||||
* Notice that Y, being an integral input, does not contribute any fraction
|
||||
* so it need not participate in the rounding.
|
||||
*
|
||||
* For even more speed, we avoid doing any multiplications in the inner loop
|
||||
* by precalculating the constants times Cb and Cr for all possible values.
|
||||
* For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table);
|
||||
* for 12-bit samples it is still acceptable. It's not very reasonable for
|
||||
* 16-bit samples, but if you want lossless storage you shouldn't be changing
|
||||
* colorspace anyway.
|
||||
* The Cr=>R and Cb=>B values can be rounded to integers in advance; the
|
||||
* values for the G calculation are left scaled up, since we must add them
|
||||
* together before rounding.
|
||||
*/
|
||||
|
||||
#define SCALEBITS 16 /* speediest right-shift on some machines */
|
||||
#define ONE_HALF ((INT32) 1 << (SCALEBITS-1))
|
||||
#define FIX(x) ((INT32) ((x) * (1L<<SCALEBITS) + 0.5))
|
||||
|
||||
|
||||
/*
|
||||
* Initialize tables for YCC->RGB colorspace conversion.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
build_ycc_rgb_table (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
|
||||
int i;
|
||||
INT32 x;
|
||||
SHIFT_TEMPS
|
||||
|
||||
cconvert->Cr_r_tab = (int *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(MAXJSAMPLE+1) * SIZEOF(int));
|
||||
cconvert->Cb_b_tab = (int *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(MAXJSAMPLE+1) * SIZEOF(int));
|
||||
cconvert->Cr_g_tab = (INT32 *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(MAXJSAMPLE+1) * SIZEOF(INT32));
|
||||
cconvert->Cb_g_tab = (INT32 *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(MAXJSAMPLE+1) * SIZEOF(INT32));
|
||||
|
||||
for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
|
||||
/* i is the actual input pixel value, in the range 0..MAXJSAMPLE */
|
||||
/* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */
|
||||
/* Cr=>R value is nearest int to 1.40200 * x */
|
||||
cconvert->Cr_r_tab[i] = (int)
|
||||
RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS);
|
||||
/* Cb=>B value is nearest int to 1.77200 * x */
|
||||
cconvert->Cb_b_tab[i] = (int)
|
||||
RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS);
|
||||
/* Cr=>G value is scaled-up -0.71414 * x */
|
||||
cconvert->Cr_g_tab[i] = (- FIX(0.71414)) * x;
|
||||
/* Cb=>G value is scaled-up -0.34414 * x */
|
||||
/* We also add in ONE_HALF so that need not do it in inner loop */
|
||||
cconvert->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert some rows of samples to the output colorspace.
|
||||
*
|
||||
* Note that we change from noninterleaved, one-plane-per-component format
|
||||
* to interleaved-pixel format. The output buffer is therefore three times
|
||||
* as wide as the input buffer.
|
||||
* A starting row offset is provided only for the input buffer. The caller
|
||||
* can easily adjust the passed output_buf value to accommodate any row
|
||||
* offset required on that side.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
ycc_rgb_convert (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION input_row,
|
||||
JSAMPARRAY output_buf, int num_rows)
|
||||
{
|
||||
my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
|
||||
register int y, cb, cr;
|
||||
register JSAMPROW outptr;
|
||||
register JSAMPROW inptr0, inptr1, inptr2;
|
||||
register JDIMENSION col;
|
||||
JDIMENSION num_cols = cinfo->output_width;
|
||||
/* copy these pointers into registers if possible */
|
||||
register JSAMPLE * range_limit = cinfo->sample_range_limit;
|
||||
register int * Crrtab = cconvert->Cr_r_tab;
|
||||
register int * Cbbtab = cconvert->Cb_b_tab;
|
||||
register INT32 * Crgtab = cconvert->Cr_g_tab;
|
||||
register INT32 * Cbgtab = cconvert->Cb_g_tab;
|
||||
SHIFT_TEMPS
|
||||
|
||||
while (--num_rows >= 0) {
|
||||
inptr0 = input_buf[0][input_row];
|
||||
inptr1 = input_buf[1][input_row];
|
||||
inptr2 = input_buf[2][input_row];
|
||||
input_row++;
|
||||
outptr = *output_buf++;
|
||||
for (col = 0; col < num_cols; col++) {
|
||||
y = GETJSAMPLE(inptr0[col]);
|
||||
cb = GETJSAMPLE(inptr1[col]);
|
||||
cr = GETJSAMPLE(inptr2[col]);
|
||||
/* Range-limiting is essential due to noise introduced by DCT losses. */
|
||||
outptr[RGB_RED] = range_limit[y + Crrtab[cr]];
|
||||
outptr[RGB_GREEN] = range_limit[y +
|
||||
((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr],
|
||||
SCALEBITS))];
|
||||
outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]];
|
||||
outptr += RGB_PIXELSIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************** Cases other than YCbCr -> RGB **************/
|
||||
|
||||
|
||||
/*
|
||||
* Color conversion for no colorspace change: just copy the data,
|
||||
* converting from separate-planes to interleaved representation.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
null_convert (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION input_row,
|
||||
JSAMPARRAY output_buf, int num_rows)
|
||||
{
|
||||
register JSAMPROW inptr, outptr;
|
||||
register JDIMENSION count;
|
||||
register int num_components = cinfo->num_components;
|
||||
JDIMENSION num_cols = cinfo->output_width;
|
||||
int ci;
|
||||
|
||||
while (--num_rows >= 0) {
|
||||
for (ci = 0; ci < num_components; ci++) {
|
||||
inptr = input_buf[ci][input_row];
|
||||
outptr = output_buf[0] + ci;
|
||||
for (count = num_cols; count > 0; count--) {
|
||||
*outptr = *inptr++; /* needn't bother with GETJSAMPLE() here */
|
||||
outptr += num_components;
|
||||
}
|
||||
}
|
||||
input_row++;
|
||||
output_buf++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Color conversion for grayscale: just copy the data.
|
||||
* This also works for YCbCr -> grayscale conversion, in which
|
||||
* we just copy the Y (luminance) component and ignore chrominance.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
grayscale_convert (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION input_row,
|
||||
JSAMPARRAY output_buf, int num_rows)
|
||||
{
|
||||
jcopy_sample_rows(input_buf[0], (int) input_row, output_buf, 0,
|
||||
num_rows, cinfo->output_width);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Adobe-style YCCK->CMYK conversion.
|
||||
* We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same
|
||||
* conversion as above, while passing K (black) unchanged.
|
||||
* We assume build_ycc_rgb_table has been called.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
ycck_cmyk_convert (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION input_row,
|
||||
JSAMPARRAY output_buf, int num_rows)
|
||||
{
|
||||
my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
|
||||
register int y, cb, cr;
|
||||
register JSAMPROW outptr;
|
||||
register JSAMPROW inptr0, inptr1, inptr2, inptr3;
|
||||
register JDIMENSION col;
|
||||
JDIMENSION num_cols = cinfo->output_width;
|
||||
/* copy these pointers into registers if possible */
|
||||
register JSAMPLE * range_limit = cinfo->sample_range_limit;
|
||||
register int * Crrtab = cconvert->Cr_r_tab;
|
||||
register int * Cbbtab = cconvert->Cb_b_tab;
|
||||
register INT32 * Crgtab = cconvert->Cr_g_tab;
|
||||
register INT32 * Cbgtab = cconvert->Cb_g_tab;
|
||||
SHIFT_TEMPS
|
||||
|
||||
while (--num_rows >= 0) {
|
||||
inptr0 = input_buf[0][input_row];
|
||||
inptr1 = input_buf[1][input_row];
|
||||
inptr2 = input_buf[2][input_row];
|
||||
inptr3 = input_buf[3][input_row];
|
||||
input_row++;
|
||||
outptr = *output_buf++;
|
||||
for (col = 0; col < num_cols; col++) {
|
||||
y = GETJSAMPLE(inptr0[col]);
|
||||
cb = GETJSAMPLE(inptr1[col]);
|
||||
cr = GETJSAMPLE(inptr2[col]);
|
||||
/* Range-limiting is essential due to noise introduced by DCT losses. */
|
||||
outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])]; /* red */
|
||||
outptr[1] = range_limit[MAXJSAMPLE - (y + /* green */
|
||||
((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr],
|
||||
SCALEBITS)))];
|
||||
outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */
|
||||
/* K passes through unchanged */
|
||||
outptr[3] = inptr3[col]; /* don't need GETJSAMPLE here */
|
||||
outptr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Empty method for start_pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_dcolor (j_decompress_ptr cinfo)
|
||||
{
|
||||
/* no work needed */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Module initialization routine for output colorspace conversion.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_color_deconverter (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_cconvert_ptr cconvert;
|
||||
int ci;
|
||||
|
||||
cconvert = (my_cconvert_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_color_deconverter));
|
||||
cinfo->cconvert = (struct jpeg_color_deconverter *) cconvert;
|
||||
cconvert->pub.start_pass = start_pass_dcolor;
|
||||
|
||||
/* Make sure num_components agrees with jpeg_color_space */
|
||||
switch (cinfo->jpeg_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
if (cinfo->num_components != 1)
|
||||
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
|
||||
break;
|
||||
|
||||
case JCS_RGB:
|
||||
case JCS_YCbCr:
|
||||
if (cinfo->num_components != 3)
|
||||
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
|
||||
break;
|
||||
|
||||
case JCS_CMYK:
|
||||
case JCS_YCCK:
|
||||
if (cinfo->num_components != 4)
|
||||
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
|
||||
break;
|
||||
|
||||
default: /* JCS_UNKNOWN can be anything */
|
||||
if (cinfo->num_components < 1)
|
||||
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set out_color_components and conversion method based on requested space.
|
||||
* Also clear the component_needed flags for any unused components,
|
||||
* so that earlier pipeline stages can avoid useless computation.
|
||||
*/
|
||||
|
||||
switch (cinfo->out_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
cinfo->out_color_components = 1;
|
||||
if (cinfo->jpeg_color_space == JCS_GRAYSCALE ||
|
||||
cinfo->jpeg_color_space == JCS_YCbCr) {
|
||||
cconvert->pub.color_convert = grayscale_convert;
|
||||
/* For color->grayscale conversion, only the Y (0) component is needed */
|
||||
for (ci = 1; ci < cinfo->num_components; ci++)
|
||||
cinfo->comp_info[ci].component_needed = FALSE;
|
||||
} else
|
||||
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
|
||||
break;
|
||||
|
||||
case JCS_RGB:
|
||||
cinfo->out_color_components = RGB_PIXELSIZE;
|
||||
if (cinfo->jpeg_color_space == JCS_YCbCr) {
|
||||
cconvert->pub.color_convert = ycc_rgb_convert;
|
||||
build_ycc_rgb_table(cinfo);
|
||||
} else if (cinfo->jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3) {
|
||||
cconvert->pub.color_convert = null_convert;
|
||||
} else
|
||||
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
|
||||
break;
|
||||
|
||||
case JCS_CMYK:
|
||||
cinfo->out_color_components = 4;
|
||||
if (cinfo->jpeg_color_space == JCS_YCCK) {
|
||||
cconvert->pub.color_convert = ycck_cmyk_convert;
|
||||
build_ycc_rgb_table(cinfo);
|
||||
} else if (cinfo->jpeg_color_space == JCS_CMYK) {
|
||||
cconvert->pub.color_convert = null_convert;
|
||||
} else
|
||||
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Permit null conversion to same output space */
|
||||
if (cinfo->out_color_space == cinfo->jpeg_color_space) {
|
||||
cinfo->out_color_components = cinfo->num_components;
|
||||
cconvert->pub.color_convert = null_convert;
|
||||
} else /* unsupported non-null conversion */
|
||||
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cinfo->quantize_colors)
|
||||
cinfo->output_components = 1; /* single colormapped output component */
|
||||
else
|
||||
cinfo->output_components = cinfo->out_color_components;
|
||||
}
|
||||
176
neo/renderer/jpeg-6/jdct.h
Normal file
176
neo/renderer/jpeg-6/jdct.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* jdct.h
|
||||
*
|
||||
* Copyright (C) 1994, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This include file contains common declarations for the forward and
|
||||
* inverse DCT modules. These declarations are private to the DCT managers
|
||||
* (jcdctmgr.c, jddctmgr.c) and the individual DCT algorithms.
|
||||
* The individual DCT algorithms are kept in separate files to ease
|
||||
* machine-dependent tuning (e.g., assembly coding).
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* A forward DCT routine is given a pointer to a work area of type DCTELEM[];
|
||||
* the DCT is to be performed in-place in that buffer. Type DCTELEM is int
|
||||
* for 8-bit samples, INT32 for 12-bit samples. (NOTE: Floating-point DCT
|
||||
* implementations use an array of type FAST_FLOAT, instead.)
|
||||
* The DCT inputs are expected to be signed (range +-CENTERJSAMPLE).
|
||||
* The DCT outputs are returned scaled up by a factor of 8; they therefore
|
||||
* have a range of +-8K for 8-bit data, +-128K for 12-bit data. This
|
||||
* convention improves accuracy in integer implementations and saves some
|
||||
* work in floating-point ones.
|
||||
* Quantization of the output coefficients is done by jcdctmgr.c.
|
||||
*/
|
||||
|
||||
#if BITS_IN_JSAMPLE == 8
|
||||
typedef int DCTELEM; /* 16 or 32 bits is fine */
|
||||
#else
|
||||
typedef INT32 DCTELEM; /* must have 32 bits */
|
||||
#endif
|
||||
|
||||
typedef JMETHOD(void, forward_DCT_method_ptr, (DCTELEM * data));
|
||||
typedef JMETHOD(void, float_DCT_method_ptr, (FAST_FLOAT * data));
|
||||
|
||||
|
||||
/*
|
||||
* An inverse DCT routine is given a pointer to the input JBLOCK and a pointer
|
||||
* to an output sample array. The routine must dequantize the input data as
|
||||
* well as perform the IDCT; for dequantization, it uses the multiplier table
|
||||
* pointed to by compptr->dct_table. The output data is to be placed into the
|
||||
* sample array starting at a specified column. (Any row offset needed will
|
||||
* be applied to the array pointer before it is passed to the IDCT code.)
|
||||
* Note that the number of samples emitted by the IDCT routine is
|
||||
* DCT_scaled_size * DCT_scaled_size.
|
||||
*/
|
||||
|
||||
/* typedef inverse_DCT_method_ptr is declared in jpegint.h */
|
||||
|
||||
/*
|
||||
* Each IDCT routine has its own ideas about the best dct_table element type.
|
||||
*/
|
||||
|
||||
typedef MULTIPLIER ISLOW_MULT_TYPE; /* short or int, whichever is faster */
|
||||
#if BITS_IN_JSAMPLE == 8
|
||||
typedef MULTIPLIER IFAST_MULT_TYPE; /* 16 bits is OK, use short if faster */
|
||||
#define IFAST_SCALE_BITS 2 /* fractional bits in scale factors */
|
||||
#else
|
||||
typedef INT32 IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */
|
||||
#define IFAST_SCALE_BITS 13 /* fractional bits in scale factors */
|
||||
#endif
|
||||
typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */
|
||||
|
||||
|
||||
/*
|
||||
* Each IDCT routine is responsible for range-limiting its results and
|
||||
* converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could
|
||||
* be quite far out of range if the input data is corrupt, so a bulletproof
|
||||
* range-limiting step is required. We use a mask-and-table-lookup method
|
||||
* to do the combined operations quickly. See the comments with
|
||||
* prepare_range_limit_table (in jdmaster.c) for more info.
|
||||
*/
|
||||
|
||||
#define IDCT_range_limit(cinfo) ((cinfo)->sample_range_limit + CENTERJSAMPLE)
|
||||
|
||||
#define RANGE_MASK (MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */
|
||||
|
||||
|
||||
/* Short forms of external names for systems with brain-damaged linkers. */
|
||||
|
||||
#ifdef NEED_SHORT_EXTERNAL_NAMES
|
||||
#define jpeg_fdct_islow jFDislow
|
||||
#define jpeg_fdct_ifast jFDifast
|
||||
#define jpeg_fdct_float jFDfloat
|
||||
#define jpeg_idct_islow jRDislow
|
||||
#define jpeg_idct_ifast jRDifast
|
||||
#define jpeg_idct_float jRDfloat
|
||||
#define jpeg_idct_4x4 jRD4x4
|
||||
#define jpeg_idct_2x2 jRD2x2
|
||||
#define jpeg_idct_1x1 jRD1x1
|
||||
#endif /* NEED_SHORT_EXTERNAL_NAMES */
|
||||
|
||||
/* Extern declarations for the forward and inverse DCT routines. */
|
||||
|
||||
EXTERN void jpeg_fdct_islow JPP((DCTELEM * data));
|
||||
EXTERN void jpeg_fdct_ifast JPP((DCTELEM * data));
|
||||
EXTERN void jpeg_fdct_float JPP((FAST_FLOAT * data));
|
||||
|
||||
EXTERN void jpeg_idct_islow
|
||||
JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
|
||||
EXTERN void jpeg_idct_ifast
|
||||
JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
|
||||
EXTERN void jpeg_idct_float
|
||||
JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
|
||||
EXTERN void jpeg_idct_4x4
|
||||
JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
|
||||
EXTERN void jpeg_idct_2x2
|
||||
JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
|
||||
EXTERN void jpeg_idct_1x1
|
||||
JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col));
|
||||
|
||||
|
||||
/*
|
||||
* Macros for handling fixed-point arithmetic; these are used by many
|
||||
* but not all of the DCT/IDCT modules.
|
||||
*
|
||||
* All values are expected to be of type INT32.
|
||||
* Fractional constants are scaled left by CONST_BITS bits.
|
||||
* CONST_BITS is defined within each module using these macros,
|
||||
* and may differ from one module to the next.
|
||||
*/
|
||||
|
||||
#define ONE ((INT32) 1)
|
||||
#define CONST_SCALE (ONE << CONST_BITS)
|
||||
|
||||
/* Convert a positive real constant to an integer scaled by CONST_SCALE.
|
||||
* Caution: some C compilers fail to reduce "FIX(constant)" at compile time,
|
||||
* thus causing a lot of useless floating-point operations at run time.
|
||||
*/
|
||||
|
||||
#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5))
|
||||
|
||||
/* Descale and correctly round an INT32 value that's scaled by N bits.
|
||||
* We assume RIGHT_SHIFT rounds towards minus infinity, so adding
|
||||
* the fudge factor is correct for either sign of X.
|
||||
*/
|
||||
|
||||
#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n)
|
||||
|
||||
/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result.
|
||||
* This macro is used only when the two inputs will actually be no more than
|
||||
* 16 bits wide, so that a 16x16->32 bit multiply can be used instead of a
|
||||
* full 32x32 multiply. This provides a useful speedup on many machines.
|
||||
* Unfortunately there is no way to specify a 16x16->32 multiply portably
|
||||
* in C, but some C compilers will do the right thing if you provide the
|
||||
* correct combination of casts.
|
||||
*/
|
||||
|
||||
#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */
|
||||
#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT16) (const)))
|
||||
#endif
|
||||
#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */
|
||||
#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT32) (const)))
|
||||
#endif
|
||||
|
||||
#ifndef MULTIPLY16C16 /* default definition */
|
||||
#define MULTIPLY16C16(var,const) ((var) * (const))
|
||||
#endif
|
||||
|
||||
/* Same except both inputs are variables. */
|
||||
|
||||
#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */
|
||||
#define MULTIPLY16V16(var1,var2) (((INT16) (var1)) * ((INT16) (var2)))
|
||||
#endif
|
||||
|
||||
#ifndef MULTIPLY16V16 /* default definition */
|
||||
#define MULTIPLY16V16(var1,var2) ((var1) * (var2))
|
||||
#endif
|
||||
270
neo/renderer/jpeg-6/jddctmgr.c
Normal file
270
neo/renderer/jpeg-6/jddctmgr.c
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* jddctmgr.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains the inverse-DCT management logic.
|
||||
* This code selects a particular IDCT implementation to be used,
|
||||
* and it performs related housekeeping chores. No code in this file
|
||||
* is executed per IDCT step, only during output pass setup.
|
||||
*
|
||||
* Note that the IDCT routines are responsible for performing coefficient
|
||||
* dequantization as well as the IDCT proper. This module sets up the
|
||||
* dequantization multiplier table needed by the IDCT routine.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jdct.h" /* Private declarations for DCT subsystem */
|
||||
|
||||
|
||||
/*
|
||||
* The decompressor input side (jdinput.c) saves away the appropriate
|
||||
* quantization table for each component at the start of the first scan
|
||||
* involving that component. (This is necessary in order to correctly
|
||||
* decode files that reuse Q-table slots.)
|
||||
* When we are ready to make an output pass, the saved Q-table is converted
|
||||
* to a multiplier table that will actually be used by the IDCT routine.
|
||||
* The multiplier table contents are IDCT-method-dependent. To support
|
||||
* application changes in IDCT method between scans, we can remake the
|
||||
* multiplier tables if necessary.
|
||||
* In buffered-image mode, the first output pass may occur before any data
|
||||
* has been seen for some components, and thus before their Q-tables have
|
||||
* been saved away. To handle this case, multiplier tables are preset
|
||||
* to zeroes; the result of the IDCT will be a neutral gray level.
|
||||
*/
|
||||
|
||||
|
||||
/* Private subobject for this module */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_inverse_dct pub; /* public fields */
|
||||
|
||||
/* This array contains the IDCT method code that each multiplier table
|
||||
* is currently set up for, or -1 if it's not yet set up.
|
||||
* The actual multiplier tables are pointed to by dct_table in the
|
||||
* per-component comp_info structures.
|
||||
*/
|
||||
int cur_method[MAX_COMPONENTS];
|
||||
} my_idct_controller;
|
||||
|
||||
typedef my_idct_controller * my_idct_ptr;
|
||||
|
||||
|
||||
/* Allocated multiplier tables: big enough for any supported variant */
|
||||
|
||||
typedef union {
|
||||
ISLOW_MULT_TYPE islow_array[DCTSIZE2];
|
||||
#ifdef DCT_IFAST_SUPPORTED
|
||||
IFAST_MULT_TYPE ifast_array[DCTSIZE2];
|
||||
#endif
|
||||
#ifdef DCT_FLOAT_SUPPORTED
|
||||
FLOAT_MULT_TYPE float_array[DCTSIZE2];
|
||||
#endif
|
||||
} multiplier_table;
|
||||
|
||||
|
||||
/* The current scaled-IDCT routines require ISLOW-style multiplier tables,
|
||||
* so be sure to compile that code if either ISLOW or SCALING is requested.
|
||||
*/
|
||||
#ifdef DCT_ISLOW_SUPPORTED
|
||||
#define PROVIDE_ISLOW_TABLES
|
||||
#else
|
||||
#ifdef IDCT_SCALING_SUPPORTED
|
||||
#define PROVIDE_ISLOW_TABLES
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Prepare for an output pass.
|
||||
* Here we select the proper IDCT routine for each component and build
|
||||
* a matching multiplier table.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_idct_ptr idct = (my_idct_ptr) cinfo->idct;
|
||||
int ci, i;
|
||||
jpeg_component_info *compptr;
|
||||
int method = 0;
|
||||
inverse_DCT_method_ptr method_ptr = NULL;
|
||||
JQUANT_TBL * qtbl;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* Select the proper IDCT routine for this component's scaling */
|
||||
switch (compptr->DCT_scaled_size) {
|
||||
#ifdef IDCT_SCALING_SUPPORTED
|
||||
case 1:
|
||||
method_ptr = jpeg_idct_1x1;
|
||||
method = JDCT_ISLOW; /* jidctred uses islow-style table */
|
||||
break;
|
||||
case 2:
|
||||
method_ptr = jpeg_idct_2x2;
|
||||
method = JDCT_ISLOW; /* jidctred uses islow-style table */
|
||||
break;
|
||||
case 4:
|
||||
method_ptr = jpeg_idct_4x4;
|
||||
method = JDCT_ISLOW; /* jidctred uses islow-style table */
|
||||
break;
|
||||
#endif
|
||||
case DCTSIZE:
|
||||
switch (cinfo->dct_method) {
|
||||
#ifdef DCT_ISLOW_SUPPORTED
|
||||
case JDCT_ISLOW:
|
||||
method_ptr = jpeg_idct_islow;
|
||||
method = JDCT_ISLOW;
|
||||
break;
|
||||
#endif
|
||||
#ifdef DCT_IFAST_SUPPORTED
|
||||
case JDCT_IFAST:
|
||||
method_ptr = jpeg_idct_ifast;
|
||||
method = JDCT_IFAST;
|
||||
break;
|
||||
#endif
|
||||
#ifdef DCT_FLOAT_SUPPORTED
|
||||
case JDCT_FLOAT:
|
||||
method_ptr = jpeg_idct_float;
|
||||
method = JDCT_FLOAT;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->DCT_scaled_size);
|
||||
break;
|
||||
}
|
||||
idct->pub.inverse_DCT[ci] = method_ptr;
|
||||
/* Create multiplier table from quant table.
|
||||
* However, we can skip this if the component is uninteresting
|
||||
* or if we already built the table. Also, if no quant table
|
||||
* has yet been saved for the component, we leave the
|
||||
* multiplier table all-zero; we'll be reading zeroes from the
|
||||
* coefficient controller's buffer anyway.
|
||||
*/
|
||||
if (! compptr->component_needed || idct->cur_method[ci] == method)
|
||||
continue;
|
||||
qtbl = compptr->quant_table;
|
||||
if (qtbl == NULL) /* happens if no data yet for component */
|
||||
continue;
|
||||
idct->cur_method[ci] = method;
|
||||
switch (method) {
|
||||
#ifdef PROVIDE_ISLOW_TABLES
|
||||
case JDCT_ISLOW:
|
||||
{
|
||||
/* For LL&M IDCT method, multipliers are equal to raw quantization
|
||||
* coefficients, but are stored in natural order as ints.
|
||||
*/
|
||||
ISLOW_MULT_TYPE * ismtbl = (ISLOW_MULT_TYPE *) compptr->dct_table;
|
||||
for (i = 0; i < DCTSIZE2; i++) {
|
||||
ismtbl[i] = (ISLOW_MULT_TYPE) qtbl->quantval[jpeg_zigzag_order[i]];
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef DCT_IFAST_SUPPORTED
|
||||
case JDCT_IFAST:
|
||||
{
|
||||
/* For AA&N IDCT method, multipliers are equal to quantization
|
||||
* coefficients scaled by scalefactor[row]*scalefactor[col], where
|
||||
* scalefactor[0] = 1
|
||||
* scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7
|
||||
* For integer operation, the multiplier table is to be scaled by
|
||||
* IFAST_SCALE_BITS. The multipliers are stored in natural order.
|
||||
*/
|
||||
IFAST_MULT_TYPE * ifmtbl = (IFAST_MULT_TYPE *) compptr->dct_table;
|
||||
#define CONST_BITS 14
|
||||
static const INT16 aanscales[DCTSIZE2] = {
|
||||
/* precomputed values scaled up by 14 bits */
|
||||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520,
|
||||
22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270,
|
||||
21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906,
|
||||
19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315,
|
||||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520,
|
||||
12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552,
|
||||
8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446,
|
||||
4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247
|
||||
};
|
||||
SHIFT_TEMPS
|
||||
|
||||
for (i = 0; i < DCTSIZE2; i++) {
|
||||
ifmtbl[i] = (IFAST_MULT_TYPE)
|
||||
DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[jpeg_zigzag_order[i]],
|
||||
(INT32) aanscales[i]),
|
||||
CONST_BITS-IFAST_SCALE_BITS);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef DCT_FLOAT_SUPPORTED
|
||||
case JDCT_FLOAT:
|
||||
{
|
||||
/* For float AA&N IDCT method, multipliers are equal to quantization
|
||||
* coefficients scaled by scalefactor[row]*scalefactor[col], where
|
||||
* scalefactor[0] = 1
|
||||
* scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7
|
||||
* The multipliers are stored in natural order.
|
||||
*/
|
||||
FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr->dct_table;
|
||||
int row, col;
|
||||
static const double aanscalefactor[DCTSIZE] = {
|
||||
1.0, 1.387039845, 1.306562965, 1.175875602,
|
||||
1.0, 0.785694958, 0.541196100, 0.275899379
|
||||
};
|
||||
|
||||
i = 0;
|
||||
for (row = 0; row < DCTSIZE; row++) {
|
||||
for (col = 0; col < DCTSIZE; col++) {
|
||||
fmtbl[i] = (FLOAT_MULT_TYPE)
|
||||
((double) qtbl->quantval[jpeg_zigzag_order[i]] *
|
||||
aanscalefactor[row] * aanscalefactor[col]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize IDCT manager.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_inverse_dct (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_idct_ptr idct;
|
||||
int ci;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
idct = (my_idct_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_idct_controller));
|
||||
cinfo->idct = (struct jpeg_inverse_dct *) idct;
|
||||
idct->pub.start_pass = start_pass;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* Allocate and pre-zero a multiplier table for each component */
|
||||
compptr->dct_table =
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(multiplier_table));
|
||||
MEMZERO(compptr->dct_table, SIZEOF(multiplier_table));
|
||||
/* Mark multiplier table not yet set up for any method */
|
||||
idct->cur_method[ci] = -1;
|
||||
}
|
||||
}
|
||||
574
neo/renderer/jpeg-6/jdhuff.c
Normal file
574
neo/renderer/jpeg-6/jdhuff.c
Normal file
@@ -0,0 +1,574 @@
|
||||
/*
|
||||
* jdhuff.c
|
||||
*
|
||||
* Copyright (C) 1991-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains Huffman entropy decoding routines.
|
||||
*
|
||||
* Much of the complexity here has to do with supporting input suspension.
|
||||
* If the data source module demands suspension, we want to be able to back
|
||||
* up to the start of the current MCU. To do this, we copy state variables
|
||||
* into local working storage, and update them back to the permanent
|
||||
* storage only upon successful completion of an MCU.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jdhuff.h" /* Declarations shared with jdphuff.c */
|
||||
|
||||
|
||||
/*
|
||||
* Expanded entropy decoder object for Huffman decoding.
|
||||
*
|
||||
* The savable_state subrecord contains fields that change within an MCU,
|
||||
* but must not be updated permanently until we complete the MCU.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */
|
||||
} savable_state;
|
||||
|
||||
/* This macro is to work around compilers with missing or broken
|
||||
* structure assignment. You'll need to fix this code if you have
|
||||
* such a compiler and you change MAX_COMPS_IN_SCAN.
|
||||
*/
|
||||
|
||||
#ifndef NO_STRUCT_ASSIGN
|
||||
#define ASSIGN_STATE(dest,src) ((dest) = (src))
|
||||
#else
|
||||
#if MAX_COMPS_IN_SCAN == 4
|
||||
#define ASSIGN_STATE(dest,src) \
|
||||
((dest).last_dc_val[0] = (src).last_dc_val[0], \
|
||||
(dest).last_dc_val[1] = (src).last_dc_val[1], \
|
||||
(dest).last_dc_val[2] = (src).last_dc_val[2], \
|
||||
(dest).last_dc_val[3] = (src).last_dc_val[3])
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_entropy_decoder pub; /* public fields */
|
||||
|
||||
/* These fields are loaded into local variables at start of each MCU.
|
||||
* In case of suspension, we exit WITHOUT updating them.
|
||||
*/
|
||||
bitread_perm_state bitstate; /* Bit buffer at start of MCU */
|
||||
savable_state saved; /* Other state at start of MCU */
|
||||
|
||||
/* These fields are NOT loaded into local working state. */
|
||||
unsigned int restarts_to_go; /* MCUs left in this restart interval */
|
||||
|
||||
/* Pointers to derived tables (these workspaces have image lifespan) */
|
||||
d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS];
|
||||
d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS];
|
||||
} huff_entropy_decoder;
|
||||
|
||||
typedef huff_entropy_decoder * huff_entropy_ptr;
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a Huffman-compressed scan.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_huff_decoder (j_decompress_ptr cinfo)
|
||||
{
|
||||
huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
|
||||
int ci, dctbl, actbl;
|
||||
jpeg_component_info * compptr;
|
||||
|
||||
/* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG.
|
||||
* This ought to be an error condition, but we make it a warning because
|
||||
* there are some baseline files out there with all zeroes in these bytes.
|
||||
*/
|
||||
if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 ||
|
||||
cinfo->Ah != 0 || cinfo->Al != 0)
|
||||
WARNMS(cinfo, JWRN_NOT_SEQUENTIAL);
|
||||
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
dctbl = compptr->dc_tbl_no;
|
||||
actbl = compptr->ac_tbl_no;
|
||||
/* Make sure requested tables are present */
|
||||
if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS ||
|
||||
cinfo->dc_huff_tbl_ptrs[dctbl] == NULL)
|
||||
ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl);
|
||||
if (actbl < 0 || actbl >= NUM_HUFF_TBLS ||
|
||||
cinfo->ac_huff_tbl_ptrs[actbl] == NULL)
|
||||
ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl);
|
||||
/* Compute derived values for Huffman tables */
|
||||
/* We may do this more than once for a table, but it's not expensive */
|
||||
jpeg_make_d_derived_tbl(cinfo, cinfo->dc_huff_tbl_ptrs[dctbl],
|
||||
& entropy->dc_derived_tbls[dctbl]);
|
||||
jpeg_make_d_derived_tbl(cinfo, cinfo->ac_huff_tbl_ptrs[actbl],
|
||||
& entropy->ac_derived_tbls[actbl]);
|
||||
/* Initialize DC predictions to 0 */
|
||||
entropy->saved.last_dc_val[ci] = 0;
|
||||
}
|
||||
|
||||
/* Initialize bitread state variables */
|
||||
entropy->bitstate.bits_left = 0;
|
||||
entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */
|
||||
entropy->bitstate.printed_eod = FALSE;
|
||||
|
||||
/* Initialize restart counter */
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compute the derived values for a Huffman table.
|
||||
* Note this is also used by jdphuff.c.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, JHUFF_TBL * htbl,
|
||||
d_derived_tbl ** pdtbl)
|
||||
{
|
||||
d_derived_tbl *dtbl;
|
||||
int p, i, l, si;
|
||||
int lookbits, ctr;
|
||||
char huffsize[257];
|
||||
unsigned int huffcode[257];
|
||||
unsigned int code;
|
||||
|
||||
/* Allocate a workspace if we haven't already done so. */
|
||||
if (*pdtbl == NULL)
|
||||
*pdtbl = (d_derived_tbl *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(d_derived_tbl));
|
||||
dtbl = *pdtbl;
|
||||
dtbl->pub = htbl; /* fill in back link */
|
||||
|
||||
/* Figure C.1: make table of Huffman code length for each symbol */
|
||||
/* Note that this is in code-length order. */
|
||||
|
||||
p = 0;
|
||||
for (l = 1; l <= 16; l++) {
|
||||
for (i = 1; i <= (int) htbl->bits[l]; i++)
|
||||
huffsize[p++] = (char) l;
|
||||
}
|
||||
huffsize[p] = 0;
|
||||
|
||||
/* Figure C.2: generate the codes themselves */
|
||||
/* Note that this is in code-length order. */
|
||||
|
||||
code = 0;
|
||||
si = huffsize[0];
|
||||
p = 0;
|
||||
while (huffsize[p]) {
|
||||
while (((int) huffsize[p]) == si) {
|
||||
huffcode[p++] = code;
|
||||
code++;
|
||||
}
|
||||
code <<= 1;
|
||||
si++;
|
||||
}
|
||||
|
||||
/* Figure F.15: generate decoding tables for bit-sequential decoding */
|
||||
|
||||
p = 0;
|
||||
for (l = 1; l <= 16; l++) {
|
||||
if (htbl->bits[l]) {
|
||||
dtbl->valptr[l] = p; /* huffval[] index of 1st symbol of code length l */
|
||||
dtbl->mincode[l] = huffcode[p]; /* minimum code of length l */
|
||||
p += htbl->bits[l];
|
||||
dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */
|
||||
} else {
|
||||
dtbl->maxcode[l] = -1; /* -1 if no codes of this length */
|
||||
}
|
||||
}
|
||||
dtbl->maxcode[17] = 0xFFFFFL; /* ensures jpeg_huff_decode terminates */
|
||||
|
||||
/* Compute lookahead tables to speed up decoding.
|
||||
* First we set all the table entries to 0, indicating "too long";
|
||||
* then we iterate through the Huffman codes that are short enough and
|
||||
* fill in all the entries that correspond to bit sequences starting
|
||||
* with that code.
|
||||
*/
|
||||
|
||||
MEMZERO(dtbl->look_nbits, SIZEOF(dtbl->look_nbits));
|
||||
|
||||
p = 0;
|
||||
for (l = 1; l <= HUFF_LOOKAHEAD; l++) {
|
||||
for (i = 1; i <= (int) htbl->bits[l]; i++, p++) {
|
||||
/* l = current code's length, p = its index in huffcode[] & huffval[]. */
|
||||
/* Generate left-justified code followed by all possible bit sequences */
|
||||
lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l);
|
||||
for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) {
|
||||
dtbl->look_nbits[lookbits] = l;
|
||||
dtbl->look_sym[lookbits] = htbl->huffval[p];
|
||||
lookbits++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Out-of-line code for bit fetching (shared with jdphuff.c).
|
||||
* See jdhuff.h for info about usage.
|
||||
* Note: current values of get_buffer and bits_left are passed as parameters,
|
||||
* but are returned in the corresponding fields of the state struct.
|
||||
*
|
||||
* On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width
|
||||
* of get_buffer to be used. (On machines with wider words, an even larger
|
||||
* buffer could be used.) However, on some machines 32-bit shifts are
|
||||
* quite slow and take time proportional to the number of places shifted.
|
||||
* (This is true with most PC compilers, for instance.) In this case it may
|
||||
* be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the
|
||||
* average shift distance at the cost of more calls to jpeg_fill_bit_buffer.
|
||||
*/
|
||||
|
||||
#ifdef SLOW_SHIFT_32
|
||||
#define MIN_GET_BITS 15 /* minimum allowable value */
|
||||
#else
|
||||
#define MIN_GET_BITS (BIT_BUF_SIZE-7)
|
||||
#endif
|
||||
|
||||
|
||||
GLOBAL boolean
|
||||
jpeg_fill_bit_buffer (bitread_working_state * state,
|
||||
register bit_buf_type get_buffer, register int bits_left,
|
||||
int nbits)
|
||||
/* Load up the bit buffer to a depth of at least nbits */
|
||||
{
|
||||
/* Copy heavily used state fields into locals (hopefully registers) */
|
||||
register const JOCTET * next_input_byte = state->next_input_byte;
|
||||
register size_t bytes_in_buffer = state->bytes_in_buffer;
|
||||
register int c;
|
||||
|
||||
/* Attempt to load at least MIN_GET_BITS bits into get_buffer. */
|
||||
/* (It is assumed that no request will be for more than that many bits.) */
|
||||
|
||||
while (bits_left < MIN_GET_BITS) {
|
||||
/* Attempt to read a byte */
|
||||
if (state->unread_marker != 0)
|
||||
goto no_more_data; /* can't advance past a marker */
|
||||
|
||||
if (bytes_in_buffer == 0) {
|
||||
if (! (*state->cinfo->src->fill_input_buffer) (state->cinfo))
|
||||
return FALSE;
|
||||
next_input_byte = state->cinfo->src->next_input_byte;
|
||||
bytes_in_buffer = state->cinfo->src->bytes_in_buffer;
|
||||
}
|
||||
bytes_in_buffer--;
|
||||
c = GETJOCTET(*next_input_byte++);
|
||||
|
||||
/* If it's 0xFF, check and discard stuffed zero byte */
|
||||
if (c == 0xFF) {
|
||||
do {
|
||||
if (bytes_in_buffer == 0) {
|
||||
if (! (*state->cinfo->src->fill_input_buffer) (state->cinfo))
|
||||
return FALSE;
|
||||
next_input_byte = state->cinfo->src->next_input_byte;
|
||||
bytes_in_buffer = state->cinfo->src->bytes_in_buffer;
|
||||
}
|
||||
bytes_in_buffer--;
|
||||
c = GETJOCTET(*next_input_byte++);
|
||||
} while (c == 0xFF);
|
||||
|
||||
if (c == 0) {
|
||||
/* Found FF/00, which represents an FF data byte */
|
||||
c = 0xFF;
|
||||
} else {
|
||||
/* Oops, it's actually a marker indicating end of compressed data. */
|
||||
/* Better put it back for use later */
|
||||
state->unread_marker = c;
|
||||
|
||||
no_more_data:
|
||||
/* There should be enough bits still left in the data segment; */
|
||||
/* if so, just break out of the outer while loop. */
|
||||
if (bits_left >= nbits)
|
||||
break;
|
||||
/* Uh-oh. Report corrupted data to user and stuff zeroes into
|
||||
* the data stream, so that we can produce some kind of image.
|
||||
* Note that this code will be repeated for each byte demanded
|
||||
* for the rest of the segment. We use a nonvolatile flag to ensure
|
||||
* that only one warning message appears.
|
||||
*/
|
||||
if (! *(state->printed_eod_ptr)) {
|
||||
WARNMS(state->cinfo, JWRN_HIT_MARKER);
|
||||
*(state->printed_eod_ptr) = TRUE;
|
||||
}
|
||||
c = 0; /* insert a zero byte into bit buffer */
|
||||
}
|
||||
}
|
||||
|
||||
/* OK, load c into get_buffer */
|
||||
get_buffer = (get_buffer << 8) | c;
|
||||
bits_left += 8;
|
||||
}
|
||||
|
||||
/* Unload the local registers */
|
||||
state->next_input_byte = next_input_byte;
|
||||
state->bytes_in_buffer = bytes_in_buffer;
|
||||
state->get_buffer = get_buffer;
|
||||
state->bits_left = bits_left;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Out-of-line code for Huffman code decoding.
|
||||
* See jdhuff.h for info about usage.
|
||||
*/
|
||||
|
||||
GLOBAL int
|
||||
jpeg_huff_decode (bitread_working_state * state,
|
||||
register bit_buf_type get_buffer, register int bits_left,
|
||||
d_derived_tbl * htbl, int min_bits)
|
||||
{
|
||||
register int l = min_bits;
|
||||
register INT32 code;
|
||||
|
||||
/* HUFF_DECODE has determined that the code is at least min_bits */
|
||||
/* bits long, so fetch that many bits in one swoop. */
|
||||
|
||||
CHECK_BIT_BUFFER(*state, l, return -1);
|
||||
code = GET_BITS(l);
|
||||
|
||||
/* Collect the rest of the Huffman code one bit at a time. */
|
||||
/* This is per Figure F.16 in the JPEG spec. */
|
||||
|
||||
while (code > htbl->maxcode[l]) {
|
||||
code <<= 1;
|
||||
CHECK_BIT_BUFFER(*state, 1, return -1);
|
||||
code |= GET_BITS(1);
|
||||
l++;
|
||||
}
|
||||
|
||||
/* Unload the local registers */
|
||||
state->get_buffer = get_buffer;
|
||||
state->bits_left = bits_left;
|
||||
|
||||
/* With garbage input we may reach the sentinel value l = 17. */
|
||||
|
||||
if (l > 16) {
|
||||
WARNMS(state->cinfo, JWRN_HUFF_BAD_CODE);
|
||||
return 0; /* fake a zero as the safest result */
|
||||
}
|
||||
|
||||
return htbl->pub->huffval[ htbl->valptr[l] +
|
||||
((int) (code - htbl->mincode[l])) ];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Figure F.12: extend sign bit.
|
||||
* On some machines, a shift and add will be faster than a table lookup.
|
||||
*/
|
||||
|
||||
#ifdef AVOID_TABLES
|
||||
|
||||
#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x))
|
||||
|
||||
#else
|
||||
|
||||
#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x))
|
||||
|
||||
static const int extend_test[16] = /* entry n is 2**(n-1) */
|
||||
{ 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
|
||||
0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 };
|
||||
|
||||
static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */
|
||||
{ 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1,
|
||||
((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1,
|
||||
((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1,
|
||||
((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 };
|
||||
|
||||
#endif /* AVOID_TABLES */
|
||||
|
||||
|
||||
/*
|
||||
* Check for a restart marker & resynchronize decoder.
|
||||
* Returns FALSE if must suspend.
|
||||
*/
|
||||
|
||||
LOCAL boolean
|
||||
process_restart (j_decompress_ptr cinfo)
|
||||
{
|
||||
huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
|
||||
int ci;
|
||||
|
||||
/* Throw away any unused bits remaining in bit buffer; */
|
||||
/* include any full bytes in next_marker's count of discarded bytes */
|
||||
cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8;
|
||||
entropy->bitstate.bits_left = 0;
|
||||
|
||||
/* Advance past the RSTn marker */
|
||||
if (! (*cinfo->marker->read_restart_marker) (cinfo))
|
||||
return FALSE;
|
||||
|
||||
/* Re-initialize DC predictions to 0 */
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++)
|
||||
entropy->saved.last_dc_val[ci] = 0;
|
||||
|
||||
/* Reset restart counter */
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
|
||||
/* Next segment can get another out-of-data warning */
|
||||
entropy->bitstate.printed_eod = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Decode and return one MCU's worth of Huffman-compressed coefficients.
|
||||
* The coefficients are reordered from zigzag order into natural array order,
|
||||
* but are not dequantized.
|
||||
*
|
||||
* The i'th block of the MCU is stored into the block pointed to by
|
||||
* MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER.
|
||||
* (Wholesale zeroing is usually a little faster than retail...)
|
||||
*
|
||||
* Returns FALSE if data source requested suspension. In that case no
|
||||
* changes have been made to permanent state. (Exception: some output
|
||||
* coefficients may already have been assigned. This is harmless for
|
||||
* this module, since we'll just re-assign them on the next call.)
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
{
|
||||
huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy;
|
||||
register int s, k, r;
|
||||
int blkn, ci;
|
||||
JBLOCKROW block;
|
||||
BITREAD_STATE_VARS;
|
||||
savable_state state;
|
||||
d_derived_tbl * dctbl;
|
||||
d_derived_tbl * actbl;
|
||||
jpeg_component_info * compptr;
|
||||
|
||||
/* Process restart marker if needed; may have to suspend */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0)
|
||||
if (! process_restart(cinfo))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Load up working state */
|
||||
BITREAD_LOAD_STATE(cinfo,entropy->bitstate);
|
||||
ASSIGN_STATE(state, entropy->saved);
|
||||
|
||||
/* Outer loop handles each block in the MCU */
|
||||
|
||||
for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
|
||||
block = MCU_data[blkn];
|
||||
ci = cinfo->MCU_membership[blkn];
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
dctbl = entropy->dc_derived_tbls[compptr->dc_tbl_no];
|
||||
actbl = entropy->ac_derived_tbls[compptr->ac_tbl_no];
|
||||
|
||||
/* Decode a single block's worth of coefficients */
|
||||
|
||||
/* Section F.2.2.1: decode the DC coefficient difference */
|
||||
HUFF_DECODE(s, br_state, dctbl, return FALSE, label1);
|
||||
if (s) {
|
||||
CHECK_BIT_BUFFER(br_state, s, return FALSE);
|
||||
r = GET_BITS(s);
|
||||
s = HUFF_EXTEND(r, s);
|
||||
}
|
||||
|
||||
/* Shortcut if component's values are not interesting */
|
||||
if (! compptr->component_needed)
|
||||
goto skip_ACs;
|
||||
|
||||
/* Convert DC difference to actual value, update last_dc_val */
|
||||
s += state.last_dc_val[ci];
|
||||
state.last_dc_val[ci] = s;
|
||||
/* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */
|
||||
(*block)[0] = (JCOEF) s;
|
||||
|
||||
/* Do we need to decode the AC coefficients for this component? */
|
||||
if (compptr->DCT_scaled_size > 1) {
|
||||
|
||||
/* Section F.2.2.2: decode the AC coefficients */
|
||||
/* Since zeroes are skipped, output area must be cleared beforehand */
|
||||
for (k = 1; k < DCTSIZE2; k++) {
|
||||
HUFF_DECODE(s, br_state, actbl, return FALSE, label2);
|
||||
|
||||
r = s >> 4;
|
||||
s &= 15;
|
||||
|
||||
if (s) {
|
||||
k += r;
|
||||
CHECK_BIT_BUFFER(br_state, s, return FALSE);
|
||||
r = GET_BITS(s);
|
||||
s = HUFF_EXTEND(r, s);
|
||||
/* Output coefficient in natural (dezigzagged) order.
|
||||
* Note: the extra entries in jpeg_natural_order[] will save us
|
||||
* if k >= DCTSIZE2, which could happen if the data is corrupted.
|
||||
*/
|
||||
(*block)[jpeg_natural_order[k]] = (JCOEF) s;
|
||||
} else {
|
||||
if (r != 15)
|
||||
break;
|
||||
k += 15;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
skip_ACs:
|
||||
|
||||
/* Section F.2.2.2: decode the AC coefficients */
|
||||
/* In this path we just discard the values */
|
||||
for (k = 1; k < DCTSIZE2; k++) {
|
||||
HUFF_DECODE(s, br_state, actbl, return FALSE, label3);
|
||||
|
||||
r = s >> 4;
|
||||
s &= 15;
|
||||
|
||||
if (s) {
|
||||
k += r;
|
||||
CHECK_BIT_BUFFER(br_state, s, return FALSE);
|
||||
DROP_BITS(s);
|
||||
} else {
|
||||
if (r != 15)
|
||||
break;
|
||||
k += 15;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Completed MCU, so update state */
|
||||
BITREAD_SAVE_STATE(cinfo,entropy->bitstate);
|
||||
ASSIGN_STATE(entropy->saved, state);
|
||||
|
||||
/* Account for restart interval (no-op if not using restarts) */
|
||||
entropy->restarts_to_go--;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Module initialization routine for Huffman entropy decoding.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_huff_decoder (j_decompress_ptr cinfo)
|
||||
{
|
||||
huff_entropy_ptr entropy;
|
||||
int i;
|
||||
|
||||
entropy = (huff_entropy_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(huff_entropy_decoder));
|
||||
cinfo->entropy = (struct jpeg_entropy_decoder *) entropy;
|
||||
entropy->pub.start_pass = start_pass_huff_decoder;
|
||||
entropy->pub.decode_mcu = decode_mcu;
|
||||
|
||||
/* Mark tables unallocated */
|
||||
for (i = 0; i < NUM_HUFF_TBLS; i++) {
|
||||
entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL;
|
||||
}
|
||||
}
|
||||
202
neo/renderer/jpeg-6/jdhuff.h
Normal file
202
neo/renderer/jpeg-6/jdhuff.h
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* jdhuff.h
|
||||
*
|
||||
* Copyright (C) 1991-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains declarations for Huffman entropy decoding routines
|
||||
* that are shared between the sequential decoder (jdhuff.c) and the
|
||||
* progressive decoder (jdphuff.c). No other modules need to see these.
|
||||
*/
|
||||
|
||||
/* Short forms of external names for systems with brain-damaged linkers. */
|
||||
|
||||
#ifdef NEED_SHORT_EXTERNAL_NAMES
|
||||
#define jpeg_make_d_derived_tbl jMkDDerived
|
||||
#define jpeg_fill_bit_buffer jFilBitBuf
|
||||
#define jpeg_huff_decode jHufDecode
|
||||
#endif /* NEED_SHORT_EXTERNAL_NAMES */
|
||||
|
||||
|
||||
/* Derived data constructed for each Huffman table */
|
||||
|
||||
#define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */
|
||||
|
||||
typedef struct {
|
||||
/* Basic tables: (element [0] of each array is unused) */
|
||||
INT32 mincode[17]; /* smallest code of length k */
|
||||
INT32 maxcode[18]; /* largest code of length k (-1 if none) */
|
||||
/* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */
|
||||
int valptr[17]; /* huffval[] index of 1st symbol of length k */
|
||||
|
||||
/* Link to public Huffman table (needed only in jpeg_huff_decode) */
|
||||
JHUFF_TBL *pub;
|
||||
|
||||
/* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of
|
||||
* the input data stream. If the next Huffman code is no more
|
||||
* than HUFF_LOOKAHEAD bits long, we can obtain its length and
|
||||
* the corresponding symbol directly from these tables.
|
||||
*/
|
||||
int look_nbits[1<<HUFF_LOOKAHEAD]; /* # bits, or 0 if too long */
|
||||
UINT8 look_sym[1<<HUFF_LOOKAHEAD]; /* symbol, or unused */
|
||||
} d_derived_tbl;
|
||||
|
||||
/* Expand a Huffman table definition into the derived format */
|
||||
EXTERN void jpeg_make_d_derived_tbl JPP((j_decompress_ptr cinfo,
|
||||
JHUFF_TBL * htbl, d_derived_tbl ** pdtbl));
|
||||
|
||||
|
||||
/*
|
||||
* Fetching the next N bits from the input stream is a time-critical operation
|
||||
* for the Huffman decoders. We implement it with a combination of inline
|
||||
* macros and out-of-line subroutines. Note that N (the number of bits
|
||||
* demanded at one time) never exceeds 15 for JPEG use.
|
||||
*
|
||||
* We read source bytes into get_buffer and dole out bits as needed.
|
||||
* If get_buffer already contains enough bits, they are fetched in-line
|
||||
* by the macros CHECK_BIT_BUFFER and GET_BITS. When there aren't enough
|
||||
* bits, jpeg_fill_bit_buffer is called; it will attempt to fill get_buffer
|
||||
* as full as possible (not just to the number of bits needed; this
|
||||
* prefetching reduces the overhead cost of calling jpeg_fill_bit_buffer).
|
||||
* Note that jpeg_fill_bit_buffer may return FALSE to indicate suspension.
|
||||
* On TRUE return, jpeg_fill_bit_buffer guarantees that get_buffer contains
|
||||
* at least the requested number of bits --- dummy zeroes are inserted if
|
||||
* necessary.
|
||||
*/
|
||||
|
||||
typedef INT32 bit_buf_type; /* type of bit-extraction buffer */
|
||||
#define BIT_BUF_SIZE 32 /* size of buffer in bits */
|
||||
|
||||
/* If long is > 32 bits on your machine, and shifting/masking longs is
|
||||
* reasonably fast, making bit_buf_type be long and setting BIT_BUF_SIZE
|
||||
* appropriately should be a win. Unfortunately we can't do this with
|
||||
* something like #define BIT_BUF_SIZE (sizeof(bit_buf_type)*8)
|
||||
* because not all machines measure sizeof in 8-bit bytes.
|
||||
*/
|
||||
|
||||
typedef struct { /* Bitreading state saved across MCUs */
|
||||
bit_buf_type get_buffer; /* current bit-extraction buffer */
|
||||
int bits_left; /* # of unused bits in it */
|
||||
boolean printed_eod; /* flag to suppress multiple warning msgs */
|
||||
} bitread_perm_state;
|
||||
|
||||
typedef struct { /* Bitreading working state within an MCU */
|
||||
/* current data source state */
|
||||
const JOCTET * next_input_byte; /* => next byte to read from source */
|
||||
size_t bytes_in_buffer; /* # of bytes remaining in source buffer */
|
||||
int unread_marker; /* nonzero if we have hit a marker */
|
||||
/* bit input buffer --- note these values are kept in register variables,
|
||||
* not in this struct, inside the inner loops.
|
||||
*/
|
||||
bit_buf_type get_buffer; /* current bit-extraction buffer */
|
||||
int bits_left; /* # of unused bits in it */
|
||||
/* pointers needed by jpeg_fill_bit_buffer */
|
||||
j_decompress_ptr cinfo; /* back link to decompress master record */
|
||||
boolean * printed_eod_ptr; /* => flag in permanent state */
|
||||
} bitread_working_state;
|
||||
|
||||
/* Macros to declare and load/save bitread local variables. */
|
||||
#define BITREAD_STATE_VARS \
|
||||
register bit_buf_type get_buffer; \
|
||||
register int bits_left; \
|
||||
bitread_working_state br_state
|
||||
|
||||
#define BITREAD_LOAD_STATE(cinfop,permstate) \
|
||||
br_state.cinfo = cinfop; \
|
||||
br_state.next_input_byte = cinfop->src->next_input_byte; \
|
||||
br_state.bytes_in_buffer = cinfop->src->bytes_in_buffer; \
|
||||
br_state.unread_marker = cinfop->unread_marker; \
|
||||
get_buffer = permstate.get_buffer; \
|
||||
bits_left = permstate.bits_left; \
|
||||
br_state.printed_eod_ptr = & permstate.printed_eod
|
||||
|
||||
#define BITREAD_SAVE_STATE(cinfop,permstate) \
|
||||
cinfop->src->next_input_byte = br_state.next_input_byte; \
|
||||
cinfop->src->bytes_in_buffer = br_state.bytes_in_buffer; \
|
||||
cinfop->unread_marker = br_state.unread_marker; \
|
||||
permstate.get_buffer = get_buffer; \
|
||||
permstate.bits_left = bits_left
|
||||
|
||||
/*
|
||||
* These macros provide the in-line portion of bit fetching.
|
||||
* Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer
|
||||
* before using GET_BITS, PEEK_BITS, or DROP_BITS.
|
||||
* The variables get_buffer and bits_left are assumed to be locals,
|
||||
* but the state struct might not be (jpeg_huff_decode needs this).
|
||||
* CHECK_BIT_BUFFER(state,n,action);
|
||||
* Ensure there are N bits in get_buffer; if suspend, take action.
|
||||
* val = GET_BITS(n);
|
||||
* Fetch next N bits.
|
||||
* val = PEEK_BITS(n);
|
||||
* Fetch next N bits without removing them from the buffer.
|
||||
* DROP_BITS(n);
|
||||
* Discard next N bits.
|
||||
* The value N should be a simple variable, not an expression, because it
|
||||
* is evaluated multiple times.
|
||||
*/
|
||||
|
||||
#define CHECK_BIT_BUFFER(state,nbits,action) \
|
||||
{ if (bits_left < (nbits)) { \
|
||||
if (! jpeg_fill_bit_buffer(&(state),get_buffer,bits_left,nbits)) \
|
||||
{ action; } \
|
||||
get_buffer = (state).get_buffer; bits_left = (state).bits_left; } }
|
||||
|
||||
#define GET_BITS(nbits) \
|
||||
(((int) (get_buffer >> (bits_left -= (nbits)))) & ((1<<(nbits))-1))
|
||||
|
||||
#define PEEK_BITS(nbits) \
|
||||
(((int) (get_buffer >> (bits_left - (nbits)))) & ((1<<(nbits))-1))
|
||||
|
||||
#define DROP_BITS(nbits) \
|
||||
(bits_left -= (nbits))
|
||||
|
||||
/* Load up the bit buffer to a depth of at least nbits */
|
||||
EXTERN boolean jpeg_fill_bit_buffer JPP((bitread_working_state * state,
|
||||
register bit_buf_type get_buffer, register int bits_left,
|
||||
int nbits));
|
||||
|
||||
|
||||
/*
|
||||
* Code for extracting next Huffman-coded symbol from input bit stream.
|
||||
* Again, this is time-critical and we make the main paths be macros.
|
||||
*
|
||||
* We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits
|
||||
* without looping. Usually, more than 95% of the Huffman codes will be 8
|
||||
* or fewer bits long. The few overlength codes are handled with a loop,
|
||||
* which need not be inline code.
|
||||
*
|
||||
* Notes about the HUFF_DECODE macro:
|
||||
* 1. Near the end of the data segment, we may fail to get enough bits
|
||||
* for a lookahead. In that case, we do it the hard way.
|
||||
* 2. If the lookahead table contains no entry, the next code must be
|
||||
* more than HUFF_LOOKAHEAD bits long.
|
||||
* 3. jpeg_huff_decode returns -1 if forced to suspend.
|
||||
*/
|
||||
|
||||
#define HUFF_DECODE(result,state,htbl,failaction,slowlabel) \
|
||||
{ register int nb, look; \
|
||||
if (bits_left < HUFF_LOOKAHEAD) { \
|
||||
if (! jpeg_fill_bit_buffer(&state,get_buffer,bits_left, 0)) {failaction;} \
|
||||
get_buffer = state.get_buffer; bits_left = state.bits_left; \
|
||||
if (bits_left < HUFF_LOOKAHEAD) { \
|
||||
nb = 1; goto slowlabel; \
|
||||
} \
|
||||
} \
|
||||
look = PEEK_BITS(HUFF_LOOKAHEAD); \
|
||||
if ((nb = htbl->look_nbits[look]) != 0) { \
|
||||
DROP_BITS(nb); \
|
||||
result = htbl->look_sym[look]; \
|
||||
} else { \
|
||||
nb = HUFF_LOOKAHEAD+1; \
|
||||
slowlabel: \
|
||||
if ((result=jpeg_huff_decode(&state,get_buffer,bits_left,htbl,nb)) < 0) \
|
||||
{ failaction; } \
|
||||
get_buffer = state.get_buffer; bits_left = state.bits_left; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Out-of-line case for Huffman code fetching */
|
||||
EXTERN int jpeg_huff_decode JPP((bitread_working_state * state,
|
||||
register bit_buf_type get_buffer, register int bits_left,
|
||||
d_derived_tbl * htbl, int min_bits));
|
||||
381
neo/renderer/jpeg-6/jdinput.c
Normal file
381
neo/renderer/jpeg-6/jdinput.c
Normal file
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* jdinput.c
|
||||
*
|
||||
* Copyright (C) 1991-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains input control logic for the JPEG decompressor.
|
||||
* These routines are concerned with controlling the decompressor's input
|
||||
* processing (marker reading and coefficient decoding). The actual input
|
||||
* reading is done in jdmarker.c, jdhuff.c, and jdphuff.c.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Private state */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_input_controller pub; /* public fields */
|
||||
|
||||
boolean inheaders; /* TRUE until first SOS is reached */
|
||||
} my_input_controller;
|
||||
|
||||
typedef my_input_controller * my_inputctl_ptr;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
METHODDEF int consume_markers JPP((j_decompress_ptr cinfo));
|
||||
|
||||
|
||||
/*
|
||||
* Routines to calculate various quantities related to the size of the image.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
initial_setup (j_decompress_ptr cinfo)
|
||||
/* Called once, when first SOS marker is reached */
|
||||
{
|
||||
int ci;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
/* Make sure image isn't bigger than I can handle */
|
||||
if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION ||
|
||||
(long) cinfo->image_width > (long) JPEG_MAX_DIMENSION)
|
||||
ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION);
|
||||
|
||||
/* For now, precision must match compiled-in value... */
|
||||
if (cinfo->data_precision != BITS_IN_JSAMPLE)
|
||||
ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision);
|
||||
|
||||
/* Check that number of components won't exceed internal array sizes */
|
||||
if (cinfo->num_components > MAX_COMPONENTS)
|
||||
ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components,
|
||||
MAX_COMPONENTS);
|
||||
|
||||
/* Compute maximum sampling factors; check factor validity */
|
||||
cinfo->max_h_samp_factor = 1;
|
||||
cinfo->max_v_samp_factor = 1;
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR ||
|
||||
compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR)
|
||||
ERREXIT(cinfo, JERR_BAD_SAMPLING);
|
||||
cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor,
|
||||
compptr->h_samp_factor);
|
||||
cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor,
|
||||
compptr->v_samp_factor);
|
||||
}
|
||||
|
||||
/* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE.
|
||||
* In the full decompressor, this will be overridden by jdmaster.c;
|
||||
* but in the transcoder, jdmaster.c is not used, so we must do it here.
|
||||
*/
|
||||
cinfo->min_DCT_scaled_size = DCTSIZE;
|
||||
|
||||
/* Compute dimensions of components */
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
compptr->DCT_scaled_size = DCTSIZE;
|
||||
/* Size in DCT blocks */
|
||||
compptr->width_in_blocks = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor,
|
||||
(long) (cinfo->max_h_samp_factor * DCTSIZE));
|
||||
compptr->height_in_blocks = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor,
|
||||
(long) (cinfo->max_v_samp_factor * DCTSIZE));
|
||||
/* downsampled_width and downsampled_height will also be overridden by
|
||||
* jdmaster.c if we are doing full decompression. The transcoder library
|
||||
* doesn't use these values, but the calling application might.
|
||||
*/
|
||||
/* Size in samples */
|
||||
compptr->downsampled_width = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor,
|
||||
(long) cinfo->max_h_samp_factor);
|
||||
compptr->downsampled_height = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor,
|
||||
(long) cinfo->max_v_samp_factor);
|
||||
/* Mark component needed, until color conversion says otherwise */
|
||||
compptr->component_needed = TRUE;
|
||||
/* Mark no quantization table yet saved for component */
|
||||
compptr->quant_table = NULL;
|
||||
}
|
||||
|
||||
/* Compute number of fully interleaved MCU rows. */
|
||||
cinfo->total_iMCU_rows = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height,
|
||||
(long) (cinfo->max_v_samp_factor*DCTSIZE));
|
||||
|
||||
/* Decide whether file contains multiple scans */
|
||||
if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode)
|
||||
cinfo->inputctl->has_multiple_scans = TRUE;
|
||||
else
|
||||
cinfo->inputctl->has_multiple_scans = FALSE;
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
per_scan_setup (j_decompress_ptr cinfo)
|
||||
/* Do computations that are needed before processing a JPEG scan */
|
||||
/* cinfo->comps_in_scan and cinfo->cur_comp_info[] were set from SOS marker */
|
||||
{
|
||||
int ci, mcublks, tmp;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
if (cinfo->comps_in_scan == 1) {
|
||||
|
||||
/* Noninterleaved (single-component) scan */
|
||||
compptr = cinfo->cur_comp_info[0];
|
||||
|
||||
/* Overall image size in MCUs */
|
||||
cinfo->MCUs_per_row = compptr->width_in_blocks;
|
||||
cinfo->MCU_rows_in_scan = compptr->height_in_blocks;
|
||||
|
||||
/* For noninterleaved scan, always one block per MCU */
|
||||
compptr->MCU_width = 1;
|
||||
compptr->MCU_height = 1;
|
||||
compptr->MCU_blocks = 1;
|
||||
compptr->MCU_sample_width = compptr->DCT_scaled_size;
|
||||
compptr->last_col_width = 1;
|
||||
/* For noninterleaved scans, it is convenient to define last_row_height
|
||||
* as the number of block rows present in the last iMCU row.
|
||||
*/
|
||||
tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor);
|
||||
if (tmp == 0) tmp = compptr->v_samp_factor;
|
||||
compptr->last_row_height = tmp;
|
||||
|
||||
/* Prepare array describing MCU composition */
|
||||
cinfo->blocks_in_MCU = 1;
|
||||
cinfo->MCU_membership[0] = 0;
|
||||
|
||||
} else {
|
||||
|
||||
/* Interleaved (multi-component) scan */
|
||||
if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN)
|
||||
ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan,
|
||||
MAX_COMPS_IN_SCAN);
|
||||
|
||||
/* Overall image size in MCUs */
|
||||
cinfo->MCUs_per_row = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_width,
|
||||
(long) (cinfo->max_h_samp_factor*DCTSIZE));
|
||||
cinfo->MCU_rows_in_scan = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height,
|
||||
(long) (cinfo->max_v_samp_factor*DCTSIZE));
|
||||
|
||||
cinfo->blocks_in_MCU = 0;
|
||||
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
/* Sampling factors give # of blocks of component in each MCU */
|
||||
compptr->MCU_width = compptr->h_samp_factor;
|
||||
compptr->MCU_height = compptr->v_samp_factor;
|
||||
compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height;
|
||||
compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_scaled_size;
|
||||
/* Figure number of non-dummy blocks in last MCU column & row */
|
||||
tmp = (int) (compptr->width_in_blocks % compptr->MCU_width);
|
||||
if (tmp == 0) tmp = compptr->MCU_width;
|
||||
compptr->last_col_width = tmp;
|
||||
tmp = (int) (compptr->height_in_blocks % compptr->MCU_height);
|
||||
if (tmp == 0) tmp = compptr->MCU_height;
|
||||
compptr->last_row_height = tmp;
|
||||
/* Prepare array describing MCU composition */
|
||||
mcublks = compptr->MCU_blocks;
|
||||
if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU)
|
||||
ERREXIT(cinfo, JERR_BAD_MCU_SIZE);
|
||||
while (mcublks-- > 0) {
|
||||
cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Save away a copy of the Q-table referenced by each component present
|
||||
* in the current scan, unless already saved during a prior scan.
|
||||
*
|
||||
* In a multiple-scan JPEG file, the encoder could assign different components
|
||||
* the same Q-table slot number, but change table definitions between scans
|
||||
* so that each component uses a different Q-table. (The IJG encoder is not
|
||||
* currently capable of doing this, but other encoders might.) Since we want
|
||||
* to be able to dequantize all the components at the end of the file, this
|
||||
* means that we have to save away the table actually used for each component.
|
||||
* We do this by copying the table at the start of the first scan containing
|
||||
* the component.
|
||||
* The JPEG spec prohibits the encoder from changing the contents of a Q-table
|
||||
* slot between scans of a component using that slot. If the encoder does so
|
||||
* anyway, this decoder will simply use the Q-table values that were current
|
||||
* at the start of the first scan for the component.
|
||||
*
|
||||
* The decompressor output side looks only at the saved quant tables,
|
||||
* not at the current Q-table slots.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
latch_quant_tables (j_decompress_ptr cinfo)
|
||||
{
|
||||
int ci, qtblno;
|
||||
jpeg_component_info *compptr;
|
||||
JQUANT_TBL * qtbl;
|
||||
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
/* No work if we already saved Q-table for this component */
|
||||
if (compptr->quant_table != NULL)
|
||||
continue;
|
||||
/* Make sure specified quantization table is present */
|
||||
qtblno = compptr->quant_tbl_no;
|
||||
if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS ||
|
||||
cinfo->quant_tbl_ptrs[qtblno] == NULL)
|
||||
ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno);
|
||||
/* OK, save away the quantization table */
|
||||
qtbl = (JQUANT_TBL *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(JQUANT_TBL));
|
||||
MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL));
|
||||
compptr->quant_table = qtbl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the input modules to read a scan of compressed data.
|
||||
* The first call to this is done by jdmaster.c after initializing
|
||||
* the entire decompressor (during jpeg_start_decompress).
|
||||
* Subsequent calls come from consume_markers, below.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_input_pass (j_decompress_ptr cinfo)
|
||||
{
|
||||
per_scan_setup(cinfo);
|
||||
latch_quant_tables(cinfo);
|
||||
(*cinfo->entropy->start_pass) (cinfo);
|
||||
(*cinfo->coef->start_input_pass) (cinfo);
|
||||
cinfo->inputctl->consume_input = cinfo->coef->consume_data;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish up after inputting a compressed-data scan.
|
||||
* This is called by the coefficient controller after it's read all
|
||||
* the expected data of the scan.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
finish_input_pass (j_decompress_ptr cinfo)
|
||||
{
|
||||
cinfo->inputctl->consume_input = consume_markers;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read JPEG markers before, between, or after compressed-data scans.
|
||||
* Change state as necessary when a new scan is reached.
|
||||
* Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI.
|
||||
*
|
||||
* The consume_input method pointer points either here or to the
|
||||
* coefficient controller's consume_data routine, depending on whether
|
||||
* we are reading a compressed data segment or inter-segment markers.
|
||||
*/
|
||||
|
||||
METHODDEF int
|
||||
consume_markers (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl;
|
||||
int val;
|
||||
|
||||
if (inputctl->pub.eoi_reached) /* After hitting EOI, read no further */
|
||||
return JPEG_REACHED_EOI;
|
||||
|
||||
val = (*cinfo->marker->read_markers) (cinfo);
|
||||
|
||||
switch (val) {
|
||||
case JPEG_REACHED_SOS: /* Found SOS */
|
||||
if (inputctl->inheaders) { /* 1st SOS */
|
||||
initial_setup(cinfo);
|
||||
inputctl->inheaders = FALSE;
|
||||
/* Note: start_input_pass must be called by jdmaster.c
|
||||
* before any more input can be consumed. jdapi.c is
|
||||
* responsible for enforcing this sequencing.
|
||||
*/
|
||||
} else { /* 2nd or later SOS marker */
|
||||
if (! inputctl->pub.has_multiple_scans)
|
||||
ERREXIT(cinfo, JERR_EOI_EXPECTED); /* Oops, I wasn't expecting this! */
|
||||
start_input_pass(cinfo);
|
||||
}
|
||||
break;
|
||||
case JPEG_REACHED_EOI: /* Found EOI */
|
||||
inputctl->pub.eoi_reached = TRUE;
|
||||
if (inputctl->inheaders) { /* Tables-only datastream, apparently */
|
||||
if (cinfo->marker->saw_SOF)
|
||||
ERREXIT(cinfo, JERR_SOF_NO_SOS);
|
||||
} else {
|
||||
/* Prevent infinite loop in coef ctlr's decompress_data routine
|
||||
* if user set output_scan_number larger than number of scans.
|
||||
*/
|
||||
if (cinfo->output_scan_number > cinfo->input_scan_number)
|
||||
cinfo->output_scan_number = cinfo->input_scan_number;
|
||||
}
|
||||
break;
|
||||
case JPEG_SUSPENDED:
|
||||
break;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reset state to begin a fresh datastream.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
reset_input_controller (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl;
|
||||
|
||||
inputctl->pub.consume_input = consume_markers;
|
||||
inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */
|
||||
inputctl->pub.eoi_reached = FALSE;
|
||||
inputctl->inheaders = TRUE;
|
||||
/* Reset other modules */
|
||||
(*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);
|
||||
(*cinfo->marker->reset_marker_reader) (cinfo);
|
||||
/* Reset progression state -- would be cleaner if entropy decoder did this */
|
||||
cinfo->coef_bits = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the input controller module.
|
||||
* This is called only once, when the decompression object is created.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_input_controller (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_inputctl_ptr inputctl;
|
||||
|
||||
/* Create subobject in permanent pool */
|
||||
inputctl = (my_inputctl_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
|
||||
SIZEOF(my_input_controller));
|
||||
cinfo->inputctl = (struct jpeg_input_controller *) inputctl;
|
||||
/* Initialize method pointers */
|
||||
inputctl->pub.consume_input = consume_markers;
|
||||
inputctl->pub.reset_input_controller = reset_input_controller;
|
||||
inputctl->pub.start_input_pass = start_input_pass;
|
||||
inputctl->pub.finish_input_pass = finish_input_pass;
|
||||
/* Initialize state: can't use reset_input_controller since we don't
|
||||
* want to try to reset other modules yet.
|
||||
*/
|
||||
inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */
|
||||
inputctl->pub.eoi_reached = FALSE;
|
||||
inputctl->inheaders = TRUE;
|
||||
}
|
||||
512
neo/renderer/jpeg-6/jdmainct.c
Normal file
512
neo/renderer/jpeg-6/jdmainct.c
Normal file
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
* jdmainct.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains the main buffer controller for decompression.
|
||||
* The main buffer lies between the JPEG decompressor proper and the
|
||||
* post-processor; it holds downsampled data in the JPEG colorspace.
|
||||
*
|
||||
* Note that this code is bypassed in raw-data mode, since the application
|
||||
* supplies the equivalent of the main buffer in that case.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/*
|
||||
* In the current system design, the main buffer need never be a full-image
|
||||
* buffer; any full-height buffers will be found inside the coefficient or
|
||||
* postprocessing controllers. Nonetheless, the main controller is not
|
||||
* trivial. Its responsibility is to provide context rows for upsampling/
|
||||
* rescaling, and doing this in an efficient fashion is a bit tricky.
|
||||
*
|
||||
* Postprocessor input data is counted in "row groups". A row group
|
||||
* is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size)
|
||||
* sample rows of each component. (We require DCT_scaled_size values to be
|
||||
* chosen such that these numbers are integers. In practice DCT_scaled_size
|
||||
* values will likely be powers of two, so we actually have the stronger
|
||||
* condition that DCT_scaled_size / min_DCT_scaled_size is an integer.)
|
||||
* Upsampling will typically produce max_v_samp_factor pixel rows from each
|
||||
* row group (times any additional scale factor that the upsampler is
|
||||
* applying).
|
||||
*
|
||||
* The coefficient controller will deliver data to us one iMCU row at a time;
|
||||
* each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or
|
||||
* exactly min_DCT_scaled_size row groups. (This amount of data corresponds
|
||||
* to one row of MCUs when the image is fully interleaved.) Note that the
|
||||
* number of sample rows varies across components, but the number of row
|
||||
* groups does not. Some garbage sample rows may be included in the last iMCU
|
||||
* row at the bottom of the image.
|
||||
*
|
||||
* Depending on the vertical scaling algorithm used, the upsampler may need
|
||||
* access to the sample row(s) above and below its current input row group.
|
||||
* The upsampler is required to set need_context_rows TRUE at global selection
|
||||
* time if so. When need_context_rows is FALSE, this controller can simply
|
||||
* obtain one iMCU row at a time from the coefficient controller and dole it
|
||||
* out as row groups to the postprocessor.
|
||||
*
|
||||
* When need_context_rows is TRUE, this controller guarantees that the buffer
|
||||
* passed to postprocessing contains at least one row group's worth of samples
|
||||
* above and below the row group(s) being processed. Note that the context
|
||||
* rows "above" the first passed row group appear at negative row offsets in
|
||||
* the passed buffer. At the top and bottom of the image, the required
|
||||
* context rows are manufactured by duplicating the first or last real sample
|
||||
* row; this avoids having special cases in the upsampling inner loops.
|
||||
*
|
||||
* The amount of context is fixed at one row group just because that's a
|
||||
* convenient number for this controller to work with. The existing
|
||||
* upsamplers really only need one sample row of context. An upsampler
|
||||
* supporting arbitrary output rescaling might wish for more than one row
|
||||
* group of context when shrinking the image; tough, we don't handle that.
|
||||
* (This is justified by the assumption that downsizing will be handled mostly
|
||||
* by adjusting the DCT_scaled_size values, so that the actual scale factor at
|
||||
* the upsample step needn't be much less than one.)
|
||||
*
|
||||
* To provide the desired context, we have to retain the last two row groups
|
||||
* of one iMCU row while reading in the next iMCU row. (The last row group
|
||||
* can't be processed until we have another row group for its below-context,
|
||||
* and so we have to save the next-to-last group too for its above-context.)
|
||||
* We could do this most simply by copying data around in our buffer, but
|
||||
* that'd be very slow. We can avoid copying any data by creating a rather
|
||||
* strange pointer structure. Here's how it works. We allocate a workspace
|
||||
* consisting of M+2 row groups (where M = min_DCT_scaled_size is the number
|
||||
* of row groups per iMCU row). We create two sets of redundant pointers to
|
||||
* the workspace. Labeling the physical row groups 0 to M+1, the synthesized
|
||||
* pointer lists look like this:
|
||||
* M+1 M-1
|
||||
* master pointer --> 0 master pointer --> 0
|
||||
* 1 1
|
||||
* ... ...
|
||||
* M-3 M-3
|
||||
* M-2 M
|
||||
* M-1 M+1
|
||||
* M M-2
|
||||
* M+1 M-1
|
||||
* 0 0
|
||||
* We read alternate iMCU rows using each master pointer; thus the last two
|
||||
* row groups of the previous iMCU row remain un-overwritten in the workspace.
|
||||
* The pointer lists are set up so that the required context rows appear to
|
||||
* be adjacent to the proper places when we pass the pointer lists to the
|
||||
* upsampler.
|
||||
*
|
||||
* The above pictures describe the normal state of the pointer lists.
|
||||
* At top and bottom of the image, we diddle the pointer lists to duplicate
|
||||
* the first or last sample row as necessary (this is cheaper than copying
|
||||
* sample rows around).
|
||||
*
|
||||
* This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1. In that
|
||||
* situation each iMCU row provides only one row group so the buffering logic
|
||||
* must be different (eg, we must read two iMCU rows before we can emit the
|
||||
* first row group). For now, we simply do not support providing context
|
||||
* rows when min_DCT_scaled_size is 1. That combination seems unlikely to
|
||||
* be worth providing --- if someone wants a 1/8th-size preview, they probably
|
||||
* want it quick and dirty, so a context-free upsampler is sufficient.
|
||||
*/
|
||||
|
||||
|
||||
/* Private buffer controller object */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_d_main_controller pub; /* public fields */
|
||||
|
||||
/* Pointer to allocated workspace (M or M+2 row groups). */
|
||||
JSAMPARRAY buffer[MAX_COMPONENTS];
|
||||
|
||||
boolean buffer_full; /* Have we gotten an iMCU row from decoder? */
|
||||
JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */
|
||||
|
||||
/* Remaining fields are only used in the context case. */
|
||||
|
||||
/* These are the master pointers to the funny-order pointer lists. */
|
||||
JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */
|
||||
|
||||
int whichptr; /* indicates which pointer set is now in use */
|
||||
int context_state; /* process_data state machine status */
|
||||
JDIMENSION rowgroups_avail; /* row groups available to postprocessor */
|
||||
JDIMENSION iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */
|
||||
} my_main_controller;
|
||||
|
||||
typedef my_main_controller * my_main_ptr;
|
||||
|
||||
/* context_state values: */
|
||||
#define CTX_PREPARE_FOR_IMCU 0 /* need to prepare for MCU row */
|
||||
#define CTX_PROCESS_IMCU 1 /* feeding iMCU to postprocessor */
|
||||
#define CTX_POSTPONED_ROW 2 /* feeding postponed row group */
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
METHODDEF void process_data_simple_main
|
||||
JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf,
|
||||
JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail));
|
||||
METHODDEF void process_data_context_main
|
||||
JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf,
|
||||
JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail));
|
||||
#ifdef QUANT_2PASS_SUPPORTED
|
||||
METHODDEF void process_data_crank_post
|
||||
JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf,
|
||||
JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail));
|
||||
#endif
|
||||
|
||||
|
||||
LOCAL void
|
||||
alloc_funny_pointers (j_decompress_ptr cinfo)
|
||||
/* Allocate space for the funny pointer lists.
|
||||
* This is done only once, not once per pass.
|
||||
*/
|
||||
{
|
||||
my_main_ptr main = (my_main_ptr) cinfo->main;
|
||||
int ci, rgroup;
|
||||
int M = cinfo->min_DCT_scaled_size;
|
||||
jpeg_component_info *compptr;
|
||||
JSAMPARRAY xbuf;
|
||||
|
||||
/* Get top-level space for component array pointers.
|
||||
* We alloc both arrays with one call to save a few cycles.
|
||||
*/
|
||||
main->xbuffer[0] = (JSAMPIMAGE)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
cinfo->num_components * 2 * SIZEOF(JSAMPARRAY));
|
||||
main->xbuffer[1] = main->xbuffer[0] + cinfo->num_components;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) /
|
||||
cinfo->min_DCT_scaled_size; /* height of a row group of component */
|
||||
/* Get space for pointer lists --- M+4 row groups in each list.
|
||||
* We alloc both pointer lists with one call to save a few cycles.
|
||||
*/
|
||||
xbuf = (JSAMPARRAY)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW));
|
||||
xbuf += rgroup; /* want one row group at negative offsets */
|
||||
main->xbuffer[0][ci] = xbuf;
|
||||
xbuf += rgroup * (M + 4);
|
||||
main->xbuffer[1][ci] = xbuf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
make_funny_pointers (j_decompress_ptr cinfo)
|
||||
/* Create the funny pointer lists discussed in the comments above.
|
||||
* The actual workspace is already allocated (in main->buffer),
|
||||
* and the space for the pointer lists is allocated too.
|
||||
* This routine just fills in the curiously ordered lists.
|
||||
* This will be repeated at the beginning of each pass.
|
||||
*/
|
||||
{
|
||||
my_main_ptr main = (my_main_ptr) cinfo->main;
|
||||
int ci, i, rgroup;
|
||||
int M = cinfo->min_DCT_scaled_size;
|
||||
jpeg_component_info *compptr;
|
||||
JSAMPARRAY buf, xbuf0, xbuf1;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) /
|
||||
cinfo->min_DCT_scaled_size; /* height of a row group of component */
|
||||
xbuf0 = main->xbuffer[0][ci];
|
||||
xbuf1 = main->xbuffer[1][ci];
|
||||
/* First copy the workspace pointers as-is */
|
||||
buf = main->buffer[ci];
|
||||
for (i = 0; i < rgroup * (M + 2); i++) {
|
||||
xbuf0[i] = xbuf1[i] = buf[i];
|
||||
}
|
||||
/* In the second list, put the last four row groups in swapped order */
|
||||
for (i = 0; i < rgroup * 2; i++) {
|
||||
xbuf1[rgroup*(M-2) + i] = buf[rgroup*M + i];
|
||||
xbuf1[rgroup*M + i] = buf[rgroup*(M-2) + i];
|
||||
}
|
||||
/* The wraparound pointers at top and bottom will be filled later
|
||||
* (see set_wraparound_pointers, below). Initially we want the "above"
|
||||
* pointers to duplicate the first actual data line. This only needs
|
||||
* to happen in xbuffer[0].
|
||||
*/
|
||||
for (i = 0; i < rgroup; i++) {
|
||||
xbuf0[i - rgroup] = xbuf0[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
set_wraparound_pointers (j_decompress_ptr cinfo)
|
||||
/* Set up the "wraparound" pointers at top and bottom of the pointer lists.
|
||||
* This changes the pointer list state from top-of-image to the normal state.
|
||||
*/
|
||||
{
|
||||
my_main_ptr main = (my_main_ptr) cinfo->main;
|
||||
int ci, i, rgroup;
|
||||
int M = cinfo->min_DCT_scaled_size;
|
||||
jpeg_component_info *compptr;
|
||||
JSAMPARRAY xbuf0, xbuf1;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) /
|
||||
cinfo->min_DCT_scaled_size; /* height of a row group of component */
|
||||
xbuf0 = main->xbuffer[0][ci];
|
||||
xbuf1 = main->xbuffer[1][ci];
|
||||
for (i = 0; i < rgroup; i++) {
|
||||
xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i];
|
||||
xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i];
|
||||
xbuf0[rgroup*(M+2) + i] = xbuf0[i];
|
||||
xbuf1[rgroup*(M+2) + i] = xbuf1[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LOCAL void
|
||||
set_bottom_pointers (j_decompress_ptr cinfo)
|
||||
/* Change the pointer lists to duplicate the last sample row at the bottom
|
||||
* of the image. whichptr indicates which xbuffer holds the final iMCU row.
|
||||
* Also sets rowgroups_avail to indicate number of nondummy row groups in row.
|
||||
*/
|
||||
{
|
||||
my_main_ptr main = (my_main_ptr) cinfo->main;
|
||||
int ci, i, rgroup, iMCUheight, rows_left;
|
||||
jpeg_component_info *compptr;
|
||||
JSAMPARRAY xbuf;
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* Count sample rows in one iMCU row and in one row group */
|
||||
iMCUheight = compptr->v_samp_factor * compptr->DCT_scaled_size;
|
||||
rgroup = iMCUheight / cinfo->min_DCT_scaled_size;
|
||||
/* Count nondummy sample rows remaining for this component */
|
||||
rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight);
|
||||
if (rows_left == 0) rows_left = iMCUheight;
|
||||
/* Count nondummy row groups. Should get same answer for each component,
|
||||
* so we need only do it once.
|
||||
*/
|
||||
if (ci == 0) {
|
||||
main->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1);
|
||||
}
|
||||
/* Duplicate the last real sample row rgroup*2 times; this pads out the
|
||||
* last partial rowgroup and ensures at least one full rowgroup of context.
|
||||
*/
|
||||
xbuf = main->xbuffer[main->whichptr][ci];
|
||||
for (i = 0; i < rgroup * 2; i++) {
|
||||
xbuf[rows_left + i] = xbuf[rows_left-1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a processing pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_main (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)
|
||||
{
|
||||
my_main_ptr main = (my_main_ptr) cinfo->main;
|
||||
|
||||
switch (pass_mode) {
|
||||
case JBUF_PASS_THRU:
|
||||
if (cinfo->upsample->need_context_rows) {
|
||||
main->pub.process_data = process_data_context_main;
|
||||
make_funny_pointers(cinfo); /* Create the xbuffer[] lists */
|
||||
main->whichptr = 0; /* Read first iMCU row into xbuffer[0] */
|
||||
main->context_state = CTX_PREPARE_FOR_IMCU;
|
||||
main->iMCU_row_ctr = 0;
|
||||
} else {
|
||||
/* Simple case with no context needed */
|
||||
main->pub.process_data = process_data_simple_main;
|
||||
}
|
||||
main->buffer_full = FALSE; /* Mark buffer empty */
|
||||
main->rowgroup_ctr = 0;
|
||||
break;
|
||||
#ifdef QUANT_2PASS_SUPPORTED
|
||||
case JBUF_CRANK_DEST:
|
||||
/* For last pass of 2-pass quantization, just crank the postprocessor */
|
||||
main->pub.process_data = process_data_crank_post;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process some data.
|
||||
* This handles the simple case where no context is required.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
process_data_simple_main (j_decompress_ptr cinfo,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail)
|
||||
{
|
||||
my_main_ptr main = (my_main_ptr) cinfo->main;
|
||||
JDIMENSION rowgroups_avail;
|
||||
|
||||
/* Read input data if we haven't filled the main buffer yet */
|
||||
if (! main->buffer_full) {
|
||||
if (! (*cinfo->coef->decompress_data) (cinfo, main->buffer))
|
||||
return; /* suspension forced, can do nothing more */
|
||||
main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */
|
||||
}
|
||||
|
||||
/* There are always min_DCT_scaled_size row groups in an iMCU row. */
|
||||
rowgroups_avail = (JDIMENSION) cinfo->min_DCT_scaled_size;
|
||||
/* Note: at the bottom of the image, we may pass extra garbage row groups
|
||||
* to the postprocessor. The postprocessor has to check for bottom
|
||||
* of image anyway (at row resolution), so no point in us doing it too.
|
||||
*/
|
||||
|
||||
/* Feed the postprocessor */
|
||||
(*cinfo->post->post_process_data) (cinfo, main->buffer,
|
||||
&main->rowgroup_ctr, rowgroups_avail,
|
||||
output_buf, out_row_ctr, out_rows_avail);
|
||||
|
||||
/* Has postprocessor consumed all the data yet? If so, mark buffer empty */
|
||||
if (main->rowgroup_ctr >= rowgroups_avail) {
|
||||
main->buffer_full = FALSE;
|
||||
main->rowgroup_ctr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process some data.
|
||||
* This handles the case where context rows must be provided.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
process_data_context_main (j_decompress_ptr cinfo,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail)
|
||||
{
|
||||
my_main_ptr main = (my_main_ptr) cinfo->main;
|
||||
|
||||
/* Read input data if we haven't filled the main buffer yet */
|
||||
if (! main->buffer_full) {
|
||||
if (! (*cinfo->coef->decompress_data) (cinfo,
|
||||
main->xbuffer[main->whichptr]))
|
||||
return; /* suspension forced, can do nothing more */
|
||||
main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */
|
||||
main->iMCU_row_ctr++; /* count rows received */
|
||||
}
|
||||
|
||||
/* Postprocessor typically will not swallow all the input data it is handed
|
||||
* in one call (due to filling the output buffer first). Must be prepared
|
||||
* to exit and restart. This switch lets us keep track of how far we got.
|
||||
* Note that each case falls through to the next on successful completion.
|
||||
*/
|
||||
switch (main->context_state) {
|
||||
case CTX_POSTPONED_ROW:
|
||||
/* Call postprocessor using previously set pointers for postponed row */
|
||||
(*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr],
|
||||
&main->rowgroup_ctr, main->rowgroups_avail,
|
||||
output_buf, out_row_ctr, out_rows_avail);
|
||||
if (main->rowgroup_ctr < main->rowgroups_avail)
|
||||
return; /* Need to suspend */
|
||||
main->context_state = CTX_PREPARE_FOR_IMCU;
|
||||
if (*out_row_ctr >= out_rows_avail)
|
||||
return; /* Postprocessor exactly filled output buf */
|
||||
/*FALLTHROUGH*/
|
||||
case CTX_PREPARE_FOR_IMCU:
|
||||
/* Prepare to process first M-1 row groups of this iMCU row */
|
||||
main->rowgroup_ctr = 0;
|
||||
main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size - 1);
|
||||
/* Check for bottom of image: if so, tweak pointers to "duplicate"
|
||||
* the last sample row, and adjust rowgroups_avail to ignore padding rows.
|
||||
*/
|
||||
if (main->iMCU_row_ctr == cinfo->total_iMCU_rows)
|
||||
set_bottom_pointers(cinfo);
|
||||
main->context_state = CTX_PROCESS_IMCU;
|
||||
/*FALLTHROUGH*/
|
||||
case CTX_PROCESS_IMCU:
|
||||
/* Call postprocessor using previously set pointers */
|
||||
(*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr],
|
||||
&main->rowgroup_ctr, main->rowgroups_avail,
|
||||
output_buf, out_row_ctr, out_rows_avail);
|
||||
if (main->rowgroup_ctr < main->rowgroups_avail)
|
||||
return; /* Need to suspend */
|
||||
/* After the first iMCU, change wraparound pointers to normal state */
|
||||
if (main->iMCU_row_ctr == 1)
|
||||
set_wraparound_pointers(cinfo);
|
||||
/* Prepare to load new iMCU row using other xbuffer list */
|
||||
main->whichptr ^= 1; /* 0=>1 or 1=>0 */
|
||||
main->buffer_full = FALSE;
|
||||
/* Still need to process last row group of this iMCU row, */
|
||||
/* which is saved at index M+1 of the other xbuffer */
|
||||
main->rowgroup_ctr = (JDIMENSION) (cinfo->min_DCT_scaled_size + 1);
|
||||
main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size + 2);
|
||||
main->context_state = CTX_POSTPONED_ROW;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process some data.
|
||||
* Final pass of two-pass quantization: just call the postprocessor.
|
||||
* Source data will be the postprocessor controller's internal buffer.
|
||||
*/
|
||||
|
||||
#ifdef QUANT_2PASS_SUPPORTED
|
||||
|
||||
METHODDEF void
|
||||
process_data_crank_post (j_decompress_ptr cinfo,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail)
|
||||
{
|
||||
(*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE) NULL,
|
||||
(JDIMENSION *) NULL, (JDIMENSION) 0,
|
||||
output_buf, out_row_ctr, out_rows_avail);
|
||||
}
|
||||
|
||||
#endif /* QUANT_2PASS_SUPPORTED */
|
||||
|
||||
|
||||
/*
|
||||
* Initialize main buffer controller.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer)
|
||||
{
|
||||
my_main_ptr main;
|
||||
int ci, rgroup, ngroups;
|
||||
jpeg_component_info *compptr;
|
||||
|
||||
main = (my_main_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_main_controller));
|
||||
cinfo->main = (struct jpeg_d_main_controller *) main;
|
||||
main->pub.start_pass = start_pass_main;
|
||||
|
||||
if (need_full_buffer) /* shouldn't happen */
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
|
||||
/* Allocate the workspace.
|
||||
* ngroups is the number of row groups we need.
|
||||
*/
|
||||
if (cinfo->upsample->need_context_rows) {
|
||||
if (cinfo->min_DCT_scaled_size < 2) /* unsupported, see comments above */
|
||||
ERREXIT(cinfo, JERR_NOTIMPL);
|
||||
alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */
|
||||
ngroups = cinfo->min_DCT_scaled_size + 2;
|
||||
} else {
|
||||
ngroups = cinfo->min_DCT_scaled_size;
|
||||
}
|
||||
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) /
|
||||
cinfo->min_DCT_scaled_size; /* height of a row group of component */
|
||||
main->buffer[ci] = (*cinfo->mem->alloc_sarray)
|
||||
((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
compptr->width_in_blocks * compptr->DCT_scaled_size,
|
||||
(JDIMENSION) (rgroup * ngroups));
|
||||
}
|
||||
}
|
||||
1052
neo/renderer/jpeg-6/jdmarker.c
Normal file
1052
neo/renderer/jpeg-6/jdmarker.c
Normal file
File diff suppressed because it is too large
Load Diff
557
neo/renderer/jpeg-6/jdmaster.c
Normal file
557
neo/renderer/jpeg-6/jdmaster.c
Normal file
@@ -0,0 +1,557 @@
|
||||
/*
|
||||
* jdmaster.c
|
||||
*
|
||||
* Copyright (C) 1991-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains master control logic for the JPEG decompressor.
|
||||
* These routines are concerned with selecting the modules to be executed
|
||||
* and with determining the number of passes and the work to be done in each
|
||||
* pass.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Private state */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_decomp_master pub; /* public fields */
|
||||
|
||||
int pass_number; /* # of passes completed */
|
||||
|
||||
boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */
|
||||
|
||||
/* Saved references to initialized quantizer modules,
|
||||
* in case we need to switch modes.
|
||||
*/
|
||||
struct jpeg_color_quantizer * quantizer_1pass;
|
||||
struct jpeg_color_quantizer * quantizer_2pass;
|
||||
} my_decomp_master;
|
||||
|
||||
typedef my_decomp_master * my_master_ptr;
|
||||
|
||||
|
||||
/*
|
||||
* Determine whether merged upsample/color conversion should be used.
|
||||
* CRUCIAL: this must match the actual capabilities of jdmerge.c!
|
||||
*/
|
||||
|
||||
LOCAL boolean
|
||||
use_merged_upsample (j_decompress_ptr cinfo)
|
||||
{
|
||||
#ifdef UPSAMPLE_MERGING_SUPPORTED
|
||||
/* Merging is the equivalent of plain box-filter upsampling */
|
||||
if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling)
|
||||
return FALSE;
|
||||
/* jdmerge.c only supports YCC=>RGB color conversion */
|
||||
if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components != 3 ||
|
||||
cinfo->out_color_space != JCS_RGB ||
|
||||
cinfo->out_color_components != RGB_PIXELSIZE)
|
||||
return FALSE;
|
||||
/* and it only handles 2h1v or 2h2v sampling ratios */
|
||||
if (cinfo->comp_info[0].h_samp_factor != 2 ||
|
||||
cinfo->comp_info[1].h_samp_factor != 1 ||
|
||||
cinfo->comp_info[2].h_samp_factor != 1 ||
|
||||
cinfo->comp_info[0].v_samp_factor > 2 ||
|
||||
cinfo->comp_info[1].v_samp_factor != 1 ||
|
||||
cinfo->comp_info[2].v_samp_factor != 1)
|
||||
return FALSE;
|
||||
/* furthermore, it doesn't work if we've scaled the IDCTs differently */
|
||||
if (cinfo->comp_info[0].DCT_scaled_size != cinfo->min_DCT_scaled_size ||
|
||||
cinfo->comp_info[1].DCT_scaled_size != cinfo->min_DCT_scaled_size ||
|
||||
cinfo->comp_info[2].DCT_scaled_size != cinfo->min_DCT_scaled_size)
|
||||
return FALSE;
|
||||
/* ??? also need to test for upsample-time rescaling, when & if supported */
|
||||
return TRUE; /* by golly, it'll work... */
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compute output image dimensions and related values.
|
||||
* NOTE: this is exported for possible use by application.
|
||||
* Hence it mustn't do anything that can't be done twice.
|
||||
* Also note that it may be called before the master module is initialized!
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_calc_output_dimensions (j_decompress_ptr cinfo)
|
||||
/* Do computations that are needed before master selection phase */
|
||||
{
|
||||
#if 0 // JDC: commented out to remove warning
|
||||
int ci;
|
||||
jpeg_component_info *compptr;
|
||||
#endif
|
||||
|
||||
/* Prevent application from calling me at wrong times */
|
||||
if (cinfo->global_state != DSTATE_READY)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
|
||||
#ifdef IDCT_SCALING_SUPPORTED
|
||||
|
||||
/* Compute actual output image dimensions and DCT scaling choices. */
|
||||
if (cinfo->scale_num * 8 <= cinfo->scale_denom) {
|
||||
/* Provide 1/8 scaling */
|
||||
cinfo->output_width = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_width, 8L);
|
||||
cinfo->output_height = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height, 8L);
|
||||
cinfo->min_DCT_scaled_size = 1;
|
||||
} else if (cinfo->scale_num * 4 <= cinfo->scale_denom) {
|
||||
/* Provide 1/4 scaling */
|
||||
cinfo->output_width = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_width, 4L);
|
||||
cinfo->output_height = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height, 4L);
|
||||
cinfo->min_DCT_scaled_size = 2;
|
||||
} else if (cinfo->scale_num * 2 <= cinfo->scale_denom) {
|
||||
/* Provide 1/2 scaling */
|
||||
cinfo->output_width = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_width, 2L);
|
||||
cinfo->output_height = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height, 2L);
|
||||
cinfo->min_DCT_scaled_size = 4;
|
||||
} else {
|
||||
/* Provide 1/1 scaling */
|
||||
cinfo->output_width = cinfo->image_width;
|
||||
cinfo->output_height = cinfo->image_height;
|
||||
cinfo->min_DCT_scaled_size = DCTSIZE;
|
||||
}
|
||||
/* In selecting the actual DCT scaling for each component, we try to
|
||||
* scale up the chroma components via IDCT scaling rather than upsampling.
|
||||
* This saves time if the upsampler gets to use 1:1 scaling.
|
||||
* Note this code assumes that the supported DCT scalings are powers of 2.
|
||||
*/
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
int ssize = cinfo->min_DCT_scaled_size;
|
||||
while (ssize < DCTSIZE &&
|
||||
(compptr->h_samp_factor * ssize * 2 <=
|
||||
cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) &&
|
||||
(compptr->v_samp_factor * ssize * 2 <=
|
||||
cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) {
|
||||
ssize = ssize * 2;
|
||||
}
|
||||
compptr->DCT_scaled_size = ssize;
|
||||
}
|
||||
|
||||
/* Recompute downsampled dimensions of components;
|
||||
* application needs to know these if using raw downsampled data.
|
||||
*/
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* Size in samples, after IDCT scaling */
|
||||
compptr->downsampled_width = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_width *
|
||||
(long) (compptr->h_samp_factor * compptr->DCT_scaled_size),
|
||||
(long) (cinfo->max_h_samp_factor * DCTSIZE));
|
||||
compptr->downsampled_height = (JDIMENSION)
|
||||
jdiv_round_up((long) cinfo->image_height *
|
||||
(long) (compptr->v_samp_factor * compptr->DCT_scaled_size),
|
||||
(long) (cinfo->max_v_samp_factor * DCTSIZE));
|
||||
}
|
||||
|
||||
#else /* !IDCT_SCALING_SUPPORTED */
|
||||
|
||||
/* Hardwire it to "no scaling" */
|
||||
cinfo->output_width = cinfo->image_width;
|
||||
cinfo->output_height = cinfo->image_height;
|
||||
/* jdinput.c has already initialized DCT_scaled_size to DCTSIZE,
|
||||
* and has computed unscaled downsampled_width and downsampled_height.
|
||||
*/
|
||||
|
||||
#endif /* IDCT_SCALING_SUPPORTED */
|
||||
|
||||
/* Report number of components in selected colorspace. */
|
||||
/* Probably this should be in the color conversion module... */
|
||||
switch (cinfo->out_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
cinfo->out_color_components = 1;
|
||||
break;
|
||||
case JCS_RGB:
|
||||
#if RGB_PIXELSIZE != 3
|
||||
cinfo->out_color_components = RGB_PIXELSIZE;
|
||||
break;
|
||||
#endif /* else share code with YCbCr */
|
||||
case JCS_YCbCr:
|
||||
cinfo->out_color_components = 3;
|
||||
break;
|
||||
case JCS_CMYK:
|
||||
case JCS_YCCK:
|
||||
cinfo->out_color_components = 4;
|
||||
break;
|
||||
default: /* else must be same colorspace as in file */
|
||||
cinfo->out_color_components = cinfo->num_components;
|
||||
break;
|
||||
}
|
||||
cinfo->output_components = (cinfo->quantize_colors ? 1 :
|
||||
cinfo->out_color_components);
|
||||
|
||||
/* See if upsampler will want to emit more than one row at a time */
|
||||
if (use_merged_upsample(cinfo))
|
||||
cinfo->rec_outbuf_height = cinfo->max_v_samp_factor;
|
||||
else
|
||||
cinfo->rec_outbuf_height = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Several decompression processes need to range-limit values to the range
|
||||
* 0..MAXJSAMPLE; the input value may fall somewhat outside this range
|
||||
* due to noise introduced by quantization, roundoff error, etc. These
|
||||
* processes are inner loops and need to be as fast as possible. On most
|
||||
* machines, particularly CPUs with pipelines or instruction prefetch,
|
||||
* a (subscript-check-less) C table lookup
|
||||
* x = sample_range_limit[x];
|
||||
* is faster than explicit tests
|
||||
* if (x < 0) x = 0;
|
||||
* else if (x > MAXJSAMPLE) x = MAXJSAMPLE;
|
||||
* These processes all use a common table prepared by the routine below.
|
||||
*
|
||||
* For most steps we can mathematically guarantee that the initial value
|
||||
* of x is within MAXJSAMPLE+1 of the legal range, so a table running from
|
||||
* -(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient. But for the initial
|
||||
* limiting step (just after the IDCT), a wildly out-of-range value is
|
||||
* possible if the input data is corrupt. To avoid any chance of indexing
|
||||
* off the end of memory and getting a bad-pointer trap, we perform the
|
||||
* post-IDCT limiting thus:
|
||||
* x = range_limit[x & MASK];
|
||||
* where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit
|
||||
* samples. Under normal circumstances this is more than enough range and
|
||||
* a correct output will be generated; with bogus input data the mask will
|
||||
* cause wraparound, and we will safely generate a bogus-but-in-range output.
|
||||
* For the post-IDCT step, we want to convert the data from signed to unsigned
|
||||
* representation by adding CENTERJSAMPLE at the same time that we limit it.
|
||||
* So the post-IDCT limiting table ends up looking like this:
|
||||
* CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE,
|
||||
* MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times),
|
||||
* 0 (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times),
|
||||
* 0,1,...,CENTERJSAMPLE-1
|
||||
* Negative inputs select values from the upper half of the table after
|
||||
* masking.
|
||||
*
|
||||
* We can save some space by overlapping the start of the post-IDCT table
|
||||
* with the simpler range limiting table. The post-IDCT table begins at
|
||||
* sample_range_limit + CENTERJSAMPLE.
|
||||
*
|
||||
* Note that the table is allocated in near data space on PCs; it's small
|
||||
* enough and used often enough to justify this.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
prepare_range_limit_table (j_decompress_ptr cinfo)
|
||||
/* Allocate and fill in the sample_range_limit table */
|
||||
{
|
||||
JSAMPLE * table;
|
||||
int i;
|
||||
|
||||
table = (JSAMPLE *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE));
|
||||
table += (MAXJSAMPLE+1); /* allow negative subscripts of simple table */
|
||||
cinfo->sample_range_limit = table;
|
||||
/* First segment of "simple" table: limit[x] = 0 for x < 0 */
|
||||
MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE));
|
||||
/* Main part of "simple" table: limit[x] = x */
|
||||
for (i = 0; i <= MAXJSAMPLE; i++)
|
||||
table[i] = (JSAMPLE) i;
|
||||
table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */
|
||||
/* End of simple table, rest of first half of post-IDCT table */
|
||||
for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++)
|
||||
table[i] = MAXJSAMPLE;
|
||||
/* Second half of post-IDCT table */
|
||||
MEMZERO(table + (2 * (MAXJSAMPLE+1)),
|
||||
(2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE));
|
||||
MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE),
|
||||
cinfo->sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Master selection of decompression modules.
|
||||
* This is done once at jpeg_start_decompress time. We determine
|
||||
* which modules will be used and give them appropriate initialization calls.
|
||||
* We also initialize the decompressor input side to begin consuming data.
|
||||
*
|
||||
* Since jpeg_read_header has finished, we know what is in the SOF
|
||||
* and (first) SOS markers. We also have all the application parameter
|
||||
* settings.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
master_selection (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_master_ptr master = (my_master_ptr) cinfo->master;
|
||||
boolean use_c_buffer;
|
||||
long samplesperrow;
|
||||
JDIMENSION jd_samplesperrow;
|
||||
|
||||
/* Initialize dimensions and other stuff */
|
||||
jpeg_calc_output_dimensions(cinfo);
|
||||
prepare_range_limit_table(cinfo);
|
||||
|
||||
/* Width of an output scanline must be representable as JDIMENSION. */
|
||||
samplesperrow = (long) cinfo->output_width * (long) cinfo->out_color_components;
|
||||
jd_samplesperrow = (JDIMENSION) samplesperrow;
|
||||
if ((long) jd_samplesperrow != samplesperrow)
|
||||
ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
|
||||
|
||||
/* Initialize my private state */
|
||||
master->pass_number = 0;
|
||||
master->using_merged_upsample = use_merged_upsample(cinfo);
|
||||
|
||||
/* Color quantizer selection */
|
||||
master->quantizer_1pass = NULL;
|
||||
master->quantizer_2pass = NULL;
|
||||
/* No mode changes if not using buffered-image mode. */
|
||||
if (! cinfo->quantize_colors || ! cinfo->buffered_image) {
|
||||
cinfo->enable_1pass_quant = FALSE;
|
||||
cinfo->enable_external_quant = FALSE;
|
||||
cinfo->enable_2pass_quant = FALSE;
|
||||
}
|
||||
if (cinfo->quantize_colors) {
|
||||
if (cinfo->raw_data_out)
|
||||
ERREXIT(cinfo, JERR_NOTIMPL);
|
||||
/* 2-pass quantizer only works in 3-component color space. */
|
||||
if (cinfo->out_color_components != 3) {
|
||||
cinfo->enable_1pass_quant = TRUE;
|
||||
cinfo->enable_external_quant = FALSE;
|
||||
cinfo->enable_2pass_quant = FALSE;
|
||||
cinfo->colormap = NULL;
|
||||
} else if (cinfo->colormap != NULL) {
|
||||
cinfo->enable_external_quant = TRUE;
|
||||
} else if (cinfo->two_pass_quantize) {
|
||||
cinfo->enable_2pass_quant = TRUE;
|
||||
} else {
|
||||
cinfo->enable_1pass_quant = TRUE;
|
||||
}
|
||||
|
||||
if (cinfo->enable_1pass_quant) {
|
||||
#ifdef QUANT_1PASS_SUPPORTED
|
||||
jinit_1pass_quantizer(cinfo);
|
||||
master->quantizer_1pass = cinfo->cquantize;
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* We use the 2-pass code to map to external colormaps. */
|
||||
if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) {
|
||||
#ifdef QUANT_2PASS_SUPPORTED
|
||||
jinit_2pass_quantizer(cinfo);
|
||||
master->quantizer_2pass = cinfo->cquantize;
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
}
|
||||
/* If both quantizers are initialized, the 2-pass one is left active;
|
||||
* this is necessary for starting with quantization to an external map.
|
||||
*/
|
||||
}
|
||||
|
||||
/* Post-processing: in particular, color conversion first */
|
||||
if (! cinfo->raw_data_out) {
|
||||
if (master->using_merged_upsample) {
|
||||
#ifdef UPSAMPLE_MERGING_SUPPORTED
|
||||
jinit_merged_upsampler(cinfo); /* does color conversion too */
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
} else {
|
||||
jinit_color_deconverter(cinfo);
|
||||
jinit_upsampler(cinfo);
|
||||
}
|
||||
jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant);
|
||||
}
|
||||
/* Inverse DCT */
|
||||
jinit_inverse_dct(cinfo);
|
||||
/* Entropy decoding: either Huffman or arithmetic coding. */
|
||||
if (cinfo->arith_code) {
|
||||
ERREXIT(cinfo, JERR_ARITH_NOTIMPL);
|
||||
} else {
|
||||
if (cinfo->progressive_mode) {
|
||||
#ifdef D_PROGRESSIVE_SUPPORTED
|
||||
jinit_phuff_decoder(cinfo);
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
} else
|
||||
jinit_huff_decoder(cinfo);
|
||||
}
|
||||
|
||||
/* Initialize principal buffer controllers. */
|
||||
use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image;
|
||||
jinit_d_coef_controller(cinfo, use_c_buffer);
|
||||
|
||||
if (! cinfo->raw_data_out)
|
||||
jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */);
|
||||
|
||||
/* We can now tell the memory manager to allocate virtual arrays. */
|
||||
(*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo);
|
||||
|
||||
/* Initialize input side of decompressor to consume first scan. */
|
||||
(*cinfo->inputctl->start_input_pass) (cinfo);
|
||||
|
||||
#ifdef D_MULTISCAN_FILES_SUPPORTED
|
||||
/* If jpeg_start_decompress will read the whole file, initialize
|
||||
* progress monitoring appropriately. The input step is counted
|
||||
* as one pass.
|
||||
*/
|
||||
if (cinfo->progress != NULL && ! cinfo->buffered_image &&
|
||||
cinfo->inputctl->has_multiple_scans) {
|
||||
int nscans;
|
||||
/* Estimate number of scans to set pass_limit. */
|
||||
if (cinfo->progressive_mode) {
|
||||
/* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */
|
||||
nscans = 2 + 3 * cinfo->num_components;
|
||||
} else {
|
||||
/* For a nonprogressive multiscan file, estimate 1 scan per component. */
|
||||
nscans = cinfo->num_components;
|
||||
}
|
||||
cinfo->progress->pass_counter = 0L;
|
||||
cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans;
|
||||
cinfo->progress->completed_passes = 0;
|
||||
cinfo->progress->total_passes = (cinfo->enable_2pass_quant ? 3 : 2);
|
||||
/* Count the input pass as done */
|
||||
master->pass_number++;
|
||||
}
|
||||
#endif /* D_MULTISCAN_FILES_SUPPORTED */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Per-pass setup.
|
||||
* This is called at the beginning of each output pass. We determine which
|
||||
* modules will be active during this pass and give them appropriate
|
||||
* start_pass calls. We also set is_dummy_pass to indicate whether this
|
||||
* is a "real" output pass or a dummy pass for color quantization.
|
||||
* (In the latter case, jdapi.c will crank the pass to completion.)
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
prepare_for_output_pass (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_master_ptr master = (my_master_ptr) cinfo->master;
|
||||
|
||||
if (master->pub.is_dummy_pass) {
|
||||
#ifdef QUANT_2PASS_SUPPORTED
|
||||
/* Final pass of 2-pass quantization */
|
||||
master->pub.is_dummy_pass = FALSE;
|
||||
(*cinfo->cquantize->start_pass) (cinfo, FALSE);
|
||||
(*cinfo->post->start_pass) (cinfo, JBUF_CRANK_DEST);
|
||||
(*cinfo->main->start_pass) (cinfo, JBUF_CRANK_DEST);
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif /* QUANT_2PASS_SUPPORTED */
|
||||
} else {
|
||||
if (cinfo->quantize_colors && cinfo->colormap == NULL) {
|
||||
/* Select new quantization method */
|
||||
if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) {
|
||||
cinfo->cquantize = master->quantizer_2pass;
|
||||
master->pub.is_dummy_pass = TRUE;
|
||||
} else if (cinfo->enable_1pass_quant) {
|
||||
cinfo->cquantize = master->quantizer_1pass;
|
||||
} else {
|
||||
ERREXIT(cinfo, JERR_MODE_CHANGE);
|
||||
}
|
||||
}
|
||||
(*cinfo->idct->start_pass) (cinfo);
|
||||
(*cinfo->coef->start_output_pass) (cinfo);
|
||||
if (! cinfo->raw_data_out) {
|
||||
if (! master->using_merged_upsample)
|
||||
(*cinfo->cconvert->start_pass) (cinfo);
|
||||
(*cinfo->upsample->start_pass) (cinfo);
|
||||
if (cinfo->quantize_colors)
|
||||
(*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass);
|
||||
(*cinfo->post->start_pass) (cinfo,
|
||||
(master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU));
|
||||
(*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up progress monitor's pass info if present */
|
||||
if (cinfo->progress != NULL) {
|
||||
cinfo->progress->completed_passes = master->pass_number;
|
||||
cinfo->progress->total_passes = master->pass_number +
|
||||
(master->pub.is_dummy_pass ? 2 : 1);
|
||||
/* In buffered-image mode, we assume one more output pass if EOI not
|
||||
* yet reached, but no more passes if EOI has been reached.
|
||||
*/
|
||||
if (cinfo->buffered_image && ! cinfo->inputctl->eoi_reached) {
|
||||
cinfo->progress->total_passes += (cinfo->enable_2pass_quant ? 2 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finish up at end of an output pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
finish_output_pass (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_master_ptr master = (my_master_ptr) cinfo->master;
|
||||
|
||||
if (cinfo->quantize_colors)
|
||||
(*cinfo->cquantize->finish_pass) (cinfo);
|
||||
master->pass_number++;
|
||||
}
|
||||
|
||||
|
||||
#ifdef D_MULTISCAN_FILES_SUPPORTED
|
||||
|
||||
/*
|
||||
* Switch to a new external colormap between output passes.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jpeg_new_colormap (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_master_ptr master = (my_master_ptr) cinfo->master;
|
||||
|
||||
/* Prevent application from calling me at wrong times */
|
||||
if (cinfo->global_state != DSTATE_BUFIMAGE)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
|
||||
if (cinfo->quantize_colors && cinfo->enable_external_quant &&
|
||||
cinfo->colormap != NULL) {
|
||||
/* Select 2-pass quantizer for external colormap use */
|
||||
cinfo->cquantize = master->quantizer_2pass;
|
||||
/* Notify quantizer of colormap change */
|
||||
(*cinfo->cquantize->new_color_map) (cinfo);
|
||||
master->pub.is_dummy_pass = FALSE; /* just in case */
|
||||
} else
|
||||
ERREXIT(cinfo, JERR_MODE_CHANGE);
|
||||
}
|
||||
|
||||
#endif /* D_MULTISCAN_FILES_SUPPORTED */
|
||||
|
||||
|
||||
/*
|
||||
* Initialize master decompression control and select active modules.
|
||||
* This is performed at the start of jpeg_start_decompress.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_master_decompress (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_master_ptr master;
|
||||
|
||||
master = (my_master_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_decomp_master));
|
||||
cinfo->master = (struct jpeg_decomp_master *) master;
|
||||
master->pub.prepare_for_output_pass = prepare_for_output_pass;
|
||||
master->pub.finish_output_pass = finish_output_pass;
|
||||
|
||||
master->pub.is_dummy_pass = FALSE;
|
||||
|
||||
master_selection(cinfo);
|
||||
}
|
||||
400
neo/renderer/jpeg-6/jdmerge.c
Normal file
400
neo/renderer/jpeg-6/jdmerge.c
Normal file
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
* jdmerge.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains code for merged upsampling/color conversion.
|
||||
*
|
||||
* This file combines functions from jdsample.c and jdcolor.c;
|
||||
* read those files first to understand what's going on.
|
||||
*
|
||||
* When the chroma components are to be upsampled by simple replication
|
||||
* (ie, box filtering), we can save some work in color conversion by
|
||||
* calculating all the output pixels corresponding to a pair of chroma
|
||||
* samples at one time. In the conversion equations
|
||||
* R = Y + K1 * Cr
|
||||
* G = Y + K2 * Cb + K3 * Cr
|
||||
* B = Y + K4 * Cb
|
||||
* only the Y term varies among the group of pixels corresponding to a pair
|
||||
* of chroma samples, so the rest of the terms can be calculated just once.
|
||||
* At typical sampling ratios, this eliminates half or three-quarters of the
|
||||
* multiplications needed for color conversion.
|
||||
*
|
||||
* This file currently provides implementations for the following cases:
|
||||
* YCbCr => RGB color conversion only.
|
||||
* Sampling ratios of 2h1v or 2h2v.
|
||||
* No scaling needed at upsample time.
|
||||
* Corner-aligned (non-CCIR601) sampling alignment.
|
||||
* Other special cases could be added, but in most applications these are
|
||||
* the only common cases. (For uncommon cases we fall back on the more
|
||||
* general code in jdsample.c and jdcolor.c.)
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
#ifdef UPSAMPLE_MERGING_SUPPORTED
|
||||
|
||||
|
||||
/* Private subobject */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_upsampler pub; /* public fields */
|
||||
|
||||
/* Pointer to routine to do actual upsampling/conversion of one row group */
|
||||
JMETHOD(void, upmethod, (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr,
|
||||
JSAMPARRAY output_buf));
|
||||
|
||||
/* Private state for YCC->RGB conversion */
|
||||
int * Cr_r_tab; /* => table for Cr to R conversion */
|
||||
int * Cb_b_tab; /* => table for Cb to B conversion */
|
||||
INT32 * Cr_g_tab; /* => table for Cr to G conversion */
|
||||
INT32 * Cb_g_tab; /* => table for Cb to G conversion */
|
||||
|
||||
/* For 2:1 vertical sampling, we produce two output rows at a time.
|
||||
* We need a "spare" row buffer to hold the second output row if the
|
||||
* application provides just a one-row buffer; we also use the spare
|
||||
* to discard the dummy last row if the image height is odd.
|
||||
*/
|
||||
JSAMPROW spare_row;
|
||||
boolean spare_full; /* T if spare buffer is occupied */
|
||||
|
||||
JDIMENSION out_row_width; /* samples per output row */
|
||||
JDIMENSION rows_to_go; /* counts rows remaining in image */
|
||||
} my_upsampler;
|
||||
|
||||
typedef my_upsampler * my_upsample_ptr;
|
||||
|
||||
#define SCALEBITS 16 /* speediest right-shift on some machines */
|
||||
#define ONE_HALF ((INT32) 1 << (SCALEBITS-1))
|
||||
#define FIX(x) ((INT32) ((x) * (1L<<SCALEBITS) + 0.5))
|
||||
|
||||
|
||||
/*
|
||||
* Initialize tables for YCC->RGB colorspace conversion.
|
||||
* This is taken directly from jdcolor.c; see that file for more info.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
build_ycc_rgb_table (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
|
||||
int i;
|
||||
INT32 x;
|
||||
SHIFT_TEMPS
|
||||
|
||||
upsample->Cr_r_tab = (int *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(MAXJSAMPLE+1) * SIZEOF(int));
|
||||
upsample->Cb_b_tab = (int *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(MAXJSAMPLE+1) * SIZEOF(int));
|
||||
upsample->Cr_g_tab = (INT32 *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(MAXJSAMPLE+1) * SIZEOF(INT32));
|
||||
upsample->Cb_g_tab = (INT32 *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(MAXJSAMPLE+1) * SIZEOF(INT32));
|
||||
|
||||
for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
|
||||
/* i is the actual input pixel value, in the range 0..MAXJSAMPLE */
|
||||
/* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */
|
||||
/* Cr=>R value is nearest int to 1.40200 * x */
|
||||
upsample->Cr_r_tab[i] = (int)
|
||||
RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS);
|
||||
/* Cb=>B value is nearest int to 1.77200 * x */
|
||||
upsample->Cb_b_tab[i] = (int)
|
||||
RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS);
|
||||
/* Cr=>G value is scaled-up -0.71414 * x */
|
||||
upsample->Cr_g_tab[i] = (- FIX(0.71414)) * x;
|
||||
/* Cb=>G value is scaled-up -0.34414 * x */
|
||||
/* We also add in ONE_HALF so that need not do it in inner loop */
|
||||
upsample->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for an upsampling pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_merged_upsample (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
|
||||
|
||||
/* Mark the spare buffer empty */
|
||||
upsample->spare_full = FALSE;
|
||||
/* Initialize total-height counter for detecting bottom of image */
|
||||
upsample->rows_to_go = cinfo->output_height;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Control routine to do upsampling (and color conversion).
|
||||
*
|
||||
* The control routine just handles the row buffering considerations.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
merged_2v_upsample (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
|
||||
JDIMENSION in_row_groups_avail,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail)
|
||||
/* 2:1 vertical sampling case: may need a spare row. */
|
||||
{
|
||||
my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
|
||||
JSAMPROW work_ptrs[2];
|
||||
JDIMENSION num_rows; /* number of rows returned to caller */
|
||||
|
||||
if (upsample->spare_full) {
|
||||
/* If we have a spare row saved from a previous cycle, just return it. */
|
||||
jcopy_sample_rows(& upsample->spare_row, 0, output_buf + *out_row_ctr, 0,
|
||||
1, upsample->out_row_width);
|
||||
num_rows = 1;
|
||||
upsample->spare_full = FALSE;
|
||||
} else {
|
||||
/* Figure number of rows to return to caller. */
|
||||
num_rows = 2;
|
||||
/* Not more than the distance to the end of the image. */
|
||||
if (num_rows > upsample->rows_to_go)
|
||||
num_rows = upsample->rows_to_go;
|
||||
/* And not more than what the client can accept: */
|
||||
out_rows_avail -= *out_row_ctr;
|
||||
if (num_rows > out_rows_avail)
|
||||
num_rows = out_rows_avail;
|
||||
/* Create output pointer array for upsampler. */
|
||||
work_ptrs[0] = output_buf[*out_row_ctr];
|
||||
if (num_rows > 1) {
|
||||
work_ptrs[1] = output_buf[*out_row_ctr + 1];
|
||||
} else {
|
||||
work_ptrs[1] = upsample->spare_row;
|
||||
upsample->spare_full = TRUE;
|
||||
}
|
||||
/* Now do the upsampling. */
|
||||
(*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, work_ptrs);
|
||||
}
|
||||
|
||||
/* Adjust counts */
|
||||
*out_row_ctr += num_rows;
|
||||
upsample->rows_to_go -= num_rows;
|
||||
/* When the buffer is emptied, declare this input row group consumed */
|
||||
if (! upsample->spare_full)
|
||||
(*in_row_group_ctr)++;
|
||||
}
|
||||
|
||||
|
||||
METHODDEF void
|
||||
merged_1v_upsample (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
|
||||
JDIMENSION in_row_groups_avail,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail)
|
||||
/* 1:1 vertical sampling case: much easier, never need a spare row. */
|
||||
{
|
||||
my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
|
||||
|
||||
/* Just do the upsampling. */
|
||||
(*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr,
|
||||
output_buf + *out_row_ctr);
|
||||
/* Adjust counts */
|
||||
(*out_row_ctr)++;
|
||||
(*in_row_group_ctr)++;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* These are the routines invoked by the control routines to do
|
||||
* the actual upsampling/conversion. One row group is processed per call.
|
||||
*
|
||||
* Note: since we may be writing directly into application-supplied buffers,
|
||||
* we have to be honest about the output width; we can't assume the buffer
|
||||
* has been rounded up to an even width.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
h2v1_merged_upsample (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr,
|
||||
JSAMPARRAY output_buf)
|
||||
{
|
||||
my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
|
||||
register int y, cred, cgreen, cblue;
|
||||
int cb, cr;
|
||||
register JSAMPROW outptr;
|
||||
JSAMPROW inptr0, inptr1, inptr2;
|
||||
JDIMENSION col;
|
||||
/* copy these pointers into registers if possible */
|
||||
register JSAMPLE * range_limit = cinfo->sample_range_limit;
|
||||
int * Crrtab = upsample->Cr_r_tab;
|
||||
int * Cbbtab = upsample->Cb_b_tab;
|
||||
INT32 * Crgtab = upsample->Cr_g_tab;
|
||||
INT32 * Cbgtab = upsample->Cb_g_tab;
|
||||
SHIFT_TEMPS
|
||||
|
||||
inptr0 = input_buf[0][in_row_group_ctr];
|
||||
inptr1 = input_buf[1][in_row_group_ctr];
|
||||
inptr2 = input_buf[2][in_row_group_ctr];
|
||||
outptr = output_buf[0];
|
||||
/* Loop for each pair of output pixels */
|
||||
for (col = cinfo->output_width >> 1; col > 0; col--) {
|
||||
/* Do the chroma part of the calculation */
|
||||
cb = GETJSAMPLE(*inptr1++);
|
||||
cr = GETJSAMPLE(*inptr2++);
|
||||
cred = Crrtab[cr];
|
||||
cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS);
|
||||
cblue = Cbbtab[cb];
|
||||
/* Fetch 2 Y values and emit 2 pixels */
|
||||
y = GETJSAMPLE(*inptr0++);
|
||||
outptr[RGB_RED] = range_limit[y + cred];
|
||||
outptr[RGB_GREEN] = range_limit[y + cgreen];
|
||||
outptr[RGB_BLUE] = range_limit[y + cblue];
|
||||
outptr += RGB_PIXELSIZE;
|
||||
y = GETJSAMPLE(*inptr0++);
|
||||
outptr[RGB_RED] = range_limit[y + cred];
|
||||
outptr[RGB_GREEN] = range_limit[y + cgreen];
|
||||
outptr[RGB_BLUE] = range_limit[y + cblue];
|
||||
outptr += RGB_PIXELSIZE;
|
||||
}
|
||||
/* If image width is odd, do the last output column separately */
|
||||
if (cinfo->output_width & 1) {
|
||||
cb = GETJSAMPLE(*inptr1);
|
||||
cr = GETJSAMPLE(*inptr2);
|
||||
cred = Crrtab[cr];
|
||||
cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS);
|
||||
cblue = Cbbtab[cb];
|
||||
y = GETJSAMPLE(*inptr0);
|
||||
outptr[RGB_RED] = range_limit[y + cred];
|
||||
outptr[RGB_GREEN] = range_limit[y + cgreen];
|
||||
outptr[RGB_BLUE] = range_limit[y + cblue];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
h2v2_merged_upsample (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr,
|
||||
JSAMPARRAY output_buf)
|
||||
{
|
||||
my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
|
||||
register int y, cred, cgreen, cblue;
|
||||
int cb, cr;
|
||||
register JSAMPROW outptr0, outptr1;
|
||||
JSAMPROW inptr00, inptr01, inptr1, inptr2;
|
||||
JDIMENSION col;
|
||||
/* copy these pointers into registers if possible */
|
||||
register JSAMPLE * range_limit = cinfo->sample_range_limit;
|
||||
int * Crrtab = upsample->Cr_r_tab;
|
||||
int * Cbbtab = upsample->Cb_b_tab;
|
||||
INT32 * Crgtab = upsample->Cr_g_tab;
|
||||
INT32 * Cbgtab = upsample->Cb_g_tab;
|
||||
SHIFT_TEMPS
|
||||
|
||||
inptr00 = input_buf[0][in_row_group_ctr*2];
|
||||
inptr01 = input_buf[0][in_row_group_ctr*2 + 1];
|
||||
inptr1 = input_buf[1][in_row_group_ctr];
|
||||
inptr2 = input_buf[2][in_row_group_ctr];
|
||||
outptr0 = output_buf[0];
|
||||
outptr1 = output_buf[1];
|
||||
/* Loop for each group of output pixels */
|
||||
for (col = cinfo->output_width >> 1; col > 0; col--) {
|
||||
/* Do the chroma part of the calculation */
|
||||
cb = GETJSAMPLE(*inptr1++);
|
||||
cr = GETJSAMPLE(*inptr2++);
|
||||
cred = Crrtab[cr];
|
||||
cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS);
|
||||
cblue = Cbbtab[cb];
|
||||
/* Fetch 4 Y values and emit 4 pixels */
|
||||
y = GETJSAMPLE(*inptr00++);
|
||||
outptr0[RGB_RED] = range_limit[y + cred];
|
||||
outptr0[RGB_GREEN] = range_limit[y + cgreen];
|
||||
outptr0[RGB_BLUE] = range_limit[y + cblue];
|
||||
outptr0 += RGB_PIXELSIZE;
|
||||
y = GETJSAMPLE(*inptr00++);
|
||||
outptr0[RGB_RED] = range_limit[y + cred];
|
||||
outptr0[RGB_GREEN] = range_limit[y + cgreen];
|
||||
outptr0[RGB_BLUE] = range_limit[y + cblue];
|
||||
outptr0 += RGB_PIXELSIZE;
|
||||
y = GETJSAMPLE(*inptr01++);
|
||||
outptr1[RGB_RED] = range_limit[y + cred];
|
||||
outptr1[RGB_GREEN] = range_limit[y + cgreen];
|
||||
outptr1[RGB_BLUE] = range_limit[y + cblue];
|
||||
outptr1 += RGB_PIXELSIZE;
|
||||
y = GETJSAMPLE(*inptr01++);
|
||||
outptr1[RGB_RED] = range_limit[y + cred];
|
||||
outptr1[RGB_GREEN] = range_limit[y + cgreen];
|
||||
outptr1[RGB_BLUE] = range_limit[y + cblue];
|
||||
outptr1 += RGB_PIXELSIZE;
|
||||
}
|
||||
/* If image width is odd, do the last output column separately */
|
||||
if (cinfo->output_width & 1) {
|
||||
cb = GETJSAMPLE(*inptr1);
|
||||
cr = GETJSAMPLE(*inptr2);
|
||||
cred = Crrtab[cr];
|
||||
cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS);
|
||||
cblue = Cbbtab[cb];
|
||||
y = GETJSAMPLE(*inptr00);
|
||||
outptr0[RGB_RED] = range_limit[y + cred];
|
||||
outptr0[RGB_GREEN] = range_limit[y + cgreen];
|
||||
outptr0[RGB_BLUE] = range_limit[y + cblue];
|
||||
y = GETJSAMPLE(*inptr01);
|
||||
outptr1[RGB_RED] = range_limit[y + cred];
|
||||
outptr1[RGB_GREEN] = range_limit[y + cgreen];
|
||||
outptr1[RGB_BLUE] = range_limit[y + cblue];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Module initialization routine for merged upsampling/color conversion.
|
||||
*
|
||||
* NB: this is called under the conditions determined by use_merged_upsample()
|
||||
* in jdmaster.c. That routine MUST correspond to the actual capabilities
|
||||
* of this module; no safety checks are made here.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_merged_upsampler (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_upsample_ptr upsample;
|
||||
|
||||
upsample = (my_upsample_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_upsampler));
|
||||
cinfo->upsample = (struct jpeg_upsampler *) upsample;
|
||||
upsample->pub.start_pass = start_pass_merged_upsample;
|
||||
upsample->pub.need_context_rows = FALSE;
|
||||
|
||||
upsample->out_row_width = cinfo->output_width * cinfo->out_color_components;
|
||||
|
||||
if (cinfo->max_v_samp_factor == 2) {
|
||||
upsample->pub.upsample = merged_2v_upsample;
|
||||
upsample->upmethod = h2v2_merged_upsample;
|
||||
/* Allocate a spare row buffer */
|
||||
upsample->spare_row = (JSAMPROW)
|
||||
(*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(size_t) (upsample->out_row_width * SIZEOF(JSAMPLE)));
|
||||
} else {
|
||||
upsample->pub.upsample = merged_1v_upsample;
|
||||
upsample->upmethod = h2v1_merged_upsample;
|
||||
/* No spare row needed */
|
||||
upsample->spare_row = NULL;
|
||||
}
|
||||
|
||||
build_ycc_rgb_table(cinfo);
|
||||
}
|
||||
|
||||
#endif /* UPSAMPLE_MERGING_SUPPORTED */
|
||||
642
neo/renderer/jpeg-6/jdphuff.c
Normal file
642
neo/renderer/jpeg-6/jdphuff.c
Normal file
@@ -0,0 +1,642 @@
|
||||
/*
|
||||
* jdphuff.c
|
||||
*
|
||||
* Copyright (C) 1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains Huffman entropy decoding routines for progressive JPEG.
|
||||
*
|
||||
* Much of the complexity here has to do with supporting input suspension.
|
||||
* If the data source module demands suspension, we want to be able to back
|
||||
* up to the start of the current MCU. To do this, we copy state variables
|
||||
* into local working storage, and update them back to the permanent
|
||||
* storage only upon successful completion of an MCU.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jdhuff.h" /* Declarations shared with jdhuff.c */
|
||||
|
||||
|
||||
#ifdef D_PROGRESSIVE_SUPPORTED
|
||||
|
||||
/*
|
||||
* Expanded entropy decoder object for progressive Huffman decoding.
|
||||
*
|
||||
* The savable_state subrecord contains fields that change within an MCU,
|
||||
* but must not be updated permanently until we complete the MCU.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
unsigned int EOBRUN; /* remaining EOBs in EOBRUN */
|
||||
int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */
|
||||
} savable_state;
|
||||
|
||||
/* This macro is to work around compilers with missing or broken
|
||||
* structure assignment. You'll need to fix this code if you have
|
||||
* such a compiler and you change MAX_COMPS_IN_SCAN.
|
||||
*/
|
||||
|
||||
#ifndef NO_STRUCT_ASSIGN
|
||||
#define ASSIGN_STATE(dest,src) ((dest) = (src))
|
||||
#else
|
||||
#if MAX_COMPS_IN_SCAN == 4
|
||||
#define ASSIGN_STATE(dest,src) \
|
||||
((dest).EOBRUN = (src).EOBRUN, \
|
||||
(dest).last_dc_val[0] = (src).last_dc_val[0], \
|
||||
(dest).last_dc_val[1] = (src).last_dc_val[1], \
|
||||
(dest).last_dc_val[2] = (src).last_dc_val[2], \
|
||||
(dest).last_dc_val[3] = (src).last_dc_val[3])
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_entropy_decoder pub; /* public fields */
|
||||
|
||||
/* These fields are loaded into local variables at start of each MCU.
|
||||
* In case of suspension, we exit WITHOUT updating them.
|
||||
*/
|
||||
bitread_perm_state bitstate; /* Bit buffer at start of MCU */
|
||||
savable_state saved; /* Other state at start of MCU */
|
||||
|
||||
/* These fields are NOT loaded into local working state. */
|
||||
unsigned int restarts_to_go; /* MCUs left in this restart interval */
|
||||
|
||||
/* Pointers to derived tables (these workspaces have image lifespan) */
|
||||
d_derived_tbl * derived_tbls[NUM_HUFF_TBLS];
|
||||
|
||||
d_derived_tbl * ac_derived_tbl; /* active table during an AC scan */
|
||||
} phuff_entropy_decoder;
|
||||
|
||||
typedef phuff_entropy_decoder * phuff_entropy_ptr;
|
||||
|
||||
/* Forward declarations */
|
||||
METHODDEF boolean decode_mcu_DC_first JPP((j_decompress_ptr cinfo,
|
||||
JBLOCKROW *MCU_data));
|
||||
METHODDEF boolean decode_mcu_AC_first JPP((j_decompress_ptr cinfo,
|
||||
JBLOCKROW *MCU_data));
|
||||
METHODDEF boolean decode_mcu_DC_refine JPP((j_decompress_ptr cinfo,
|
||||
JBLOCKROW *MCU_data));
|
||||
METHODDEF boolean decode_mcu_AC_refine JPP((j_decompress_ptr cinfo,
|
||||
JBLOCKROW *MCU_data));
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a Huffman-compressed scan.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_phuff_decoder (j_decompress_ptr cinfo)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
boolean is_DC_band, bad;
|
||||
int ci, coefi, tbl;
|
||||
int *coef_bit_ptr;
|
||||
jpeg_component_info * compptr;
|
||||
|
||||
is_DC_band = (cinfo->Ss == 0);
|
||||
|
||||
/* Validate scan parameters */
|
||||
bad = FALSE;
|
||||
if (is_DC_band) {
|
||||
if (cinfo->Se != 0)
|
||||
bad = TRUE;
|
||||
} else {
|
||||
/* need not check Ss/Se < 0 since they came from unsigned bytes */
|
||||
if (cinfo->Ss > cinfo->Se || cinfo->Se >= DCTSIZE2)
|
||||
bad = TRUE;
|
||||
/* AC scans may have only one component */
|
||||
if (cinfo->comps_in_scan != 1)
|
||||
bad = TRUE;
|
||||
}
|
||||
if (cinfo->Ah != 0) {
|
||||
/* Successive approximation refinement scan: must have Al = Ah-1. */
|
||||
if (cinfo->Al != cinfo->Ah-1)
|
||||
bad = TRUE;
|
||||
}
|
||||
if (cinfo->Al > 13) /* need not check for < 0 */
|
||||
bad = TRUE;
|
||||
if (bad)
|
||||
ERREXIT4(cinfo, JERR_BAD_PROGRESSION,
|
||||
cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al);
|
||||
/* Update progression status, and verify that scan order is legal.
|
||||
* Note that inter-scan inconsistencies are treated as warnings
|
||||
* not fatal errors ... not clear if this is right way to behave.
|
||||
*/
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
int cindex = cinfo->cur_comp_info[ci]->component_index;
|
||||
coef_bit_ptr = & cinfo->coef_bits[cindex][0];
|
||||
if (!is_DC_band && coef_bit_ptr[0] < 0) /* AC without prior DC scan */
|
||||
WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, 0);
|
||||
for (coefi = cinfo->Ss; coefi <= cinfo->Se; coefi++) {
|
||||
int expected = (coef_bit_ptr[coefi] < 0) ? 0 : coef_bit_ptr[coefi];
|
||||
if (cinfo->Ah != expected)
|
||||
WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, coefi);
|
||||
coef_bit_ptr[coefi] = cinfo->Al;
|
||||
}
|
||||
}
|
||||
|
||||
/* Select MCU decoding routine */
|
||||
if (cinfo->Ah == 0) {
|
||||
if (is_DC_band)
|
||||
entropy->pub.decode_mcu = decode_mcu_DC_first;
|
||||
else
|
||||
entropy->pub.decode_mcu = decode_mcu_AC_first;
|
||||
} else {
|
||||
if (is_DC_band)
|
||||
entropy->pub.decode_mcu = decode_mcu_DC_refine;
|
||||
else
|
||||
entropy->pub.decode_mcu = decode_mcu_AC_refine;
|
||||
}
|
||||
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++) {
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
/* Make sure requested tables are present, and compute derived tables.
|
||||
* We may build same derived table more than once, but it's not expensive.
|
||||
*/
|
||||
if (is_DC_band) {
|
||||
if (cinfo->Ah == 0) { /* DC refinement needs no table */
|
||||
tbl = compptr->dc_tbl_no;
|
||||
if (tbl < 0 || tbl >= NUM_HUFF_TBLS ||
|
||||
cinfo->dc_huff_tbl_ptrs[tbl] == NULL)
|
||||
ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tbl);
|
||||
jpeg_make_d_derived_tbl(cinfo, cinfo->dc_huff_tbl_ptrs[tbl],
|
||||
& entropy->derived_tbls[tbl]);
|
||||
}
|
||||
} else {
|
||||
tbl = compptr->ac_tbl_no;
|
||||
if (tbl < 0 || tbl >= NUM_HUFF_TBLS ||
|
||||
cinfo->ac_huff_tbl_ptrs[tbl] == NULL)
|
||||
ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tbl);
|
||||
jpeg_make_d_derived_tbl(cinfo, cinfo->ac_huff_tbl_ptrs[tbl],
|
||||
& entropy->derived_tbls[tbl]);
|
||||
/* remember the single active table */
|
||||
entropy->ac_derived_tbl = entropy->derived_tbls[tbl];
|
||||
}
|
||||
/* Initialize DC predictions to 0 */
|
||||
entropy->saved.last_dc_val[ci] = 0;
|
||||
}
|
||||
|
||||
/* Initialize bitread state variables */
|
||||
entropy->bitstate.bits_left = 0;
|
||||
entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */
|
||||
entropy->bitstate.printed_eod = FALSE;
|
||||
|
||||
/* Initialize private state variables */
|
||||
entropy->saved.EOBRUN = 0;
|
||||
|
||||
/* Initialize restart counter */
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Figure F.12: extend sign bit.
|
||||
* On some machines, a shift and add will be faster than a table lookup.
|
||||
*/
|
||||
|
||||
#ifdef AVOID_TABLES
|
||||
|
||||
#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x))
|
||||
|
||||
#else
|
||||
|
||||
#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x))
|
||||
|
||||
static const int extend_test[16] = /* entry n is 2**(n-1) */
|
||||
{ 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
|
||||
0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 };
|
||||
|
||||
static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */
|
||||
{ 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1,
|
||||
((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1,
|
||||
((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1,
|
||||
((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 };
|
||||
|
||||
#endif /* AVOID_TABLES */
|
||||
|
||||
|
||||
/*
|
||||
* Check for a restart marker & resynchronize decoder.
|
||||
* Returns FALSE if must suspend.
|
||||
*/
|
||||
|
||||
LOCAL boolean
|
||||
process_restart (j_decompress_ptr cinfo)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
int ci;
|
||||
|
||||
/* Throw away any unused bits remaining in bit buffer; */
|
||||
/* include any full bytes in next_marker's count of discarded bytes */
|
||||
cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8;
|
||||
entropy->bitstate.bits_left = 0;
|
||||
|
||||
/* Advance past the RSTn marker */
|
||||
if (! (*cinfo->marker->read_restart_marker) (cinfo))
|
||||
return FALSE;
|
||||
|
||||
/* Re-initialize DC predictions to 0 */
|
||||
for (ci = 0; ci < cinfo->comps_in_scan; ci++)
|
||||
entropy->saved.last_dc_val[ci] = 0;
|
||||
/* Re-init EOB run count, too */
|
||||
entropy->saved.EOBRUN = 0;
|
||||
|
||||
/* Reset restart counter */
|
||||
entropy->restarts_to_go = cinfo->restart_interval;
|
||||
|
||||
/* Next segment can get another out-of-data warning */
|
||||
entropy->bitstate.printed_eod = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Huffman MCU decoding.
|
||||
* Each of these routines decodes and returns one MCU's worth of
|
||||
* Huffman-compressed coefficients.
|
||||
* The coefficients are reordered from zigzag order into natural array order,
|
||||
* but are not dequantized.
|
||||
*
|
||||
* The i'th block of the MCU is stored into the block pointed to by
|
||||
* MCU_data[i]. WE ASSUME THIS AREA IS INITIALLY ZEROED BY THE CALLER.
|
||||
*
|
||||
* We return FALSE if data source requested suspension. In that case no
|
||||
* changes have been made to permanent state. (Exception: some output
|
||||
* coefficients may already have been assigned. This is harmless for
|
||||
* spectral selection, since we'll just re-assign them on the next call.
|
||||
* Successive approximation AC refinement has to be more careful, however.)
|
||||
*/
|
||||
|
||||
/*
|
||||
* MCU decoding for DC initial scan (either spectral selection,
|
||||
* or first pass of successive approximation).
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
int Al = cinfo->Al;
|
||||
register int s, r;
|
||||
int blkn, ci;
|
||||
JBLOCKROW block;
|
||||
BITREAD_STATE_VARS;
|
||||
savable_state state;
|
||||
d_derived_tbl * tbl;
|
||||
jpeg_component_info * compptr;
|
||||
|
||||
/* Process restart marker if needed; may have to suspend */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0)
|
||||
if (! process_restart(cinfo))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Load up working state */
|
||||
BITREAD_LOAD_STATE(cinfo,entropy->bitstate);
|
||||
ASSIGN_STATE(state, entropy->saved);
|
||||
|
||||
/* Outer loop handles each block in the MCU */
|
||||
|
||||
for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
|
||||
block = MCU_data[blkn];
|
||||
ci = cinfo->MCU_membership[blkn];
|
||||
compptr = cinfo->cur_comp_info[ci];
|
||||
tbl = entropy->derived_tbls[compptr->dc_tbl_no];
|
||||
|
||||
/* Decode a single block's worth of coefficients */
|
||||
|
||||
/* Section F.2.2.1: decode the DC coefficient difference */
|
||||
HUFF_DECODE(s, br_state, tbl, return FALSE, label1);
|
||||
if (s) {
|
||||
CHECK_BIT_BUFFER(br_state, s, return FALSE);
|
||||
r = GET_BITS(s);
|
||||
s = HUFF_EXTEND(r, s);
|
||||
}
|
||||
|
||||
/* Convert DC difference to actual value, update last_dc_val */
|
||||
s += state.last_dc_val[ci];
|
||||
state.last_dc_val[ci] = s;
|
||||
/* Scale and output the DC coefficient (assumes jpeg_natural_order[0]=0) */
|
||||
(*block)[0] = (JCOEF) (s << Al);
|
||||
}
|
||||
|
||||
/* Completed MCU, so update state */
|
||||
BITREAD_SAVE_STATE(cinfo,entropy->bitstate);
|
||||
ASSIGN_STATE(entropy->saved, state);
|
||||
|
||||
/* Account for restart interval (no-op if not using restarts) */
|
||||
entropy->restarts_to_go--;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MCU decoding for AC initial scan (either spectral selection,
|
||||
* or first pass of successive approximation).
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
int Se = cinfo->Se;
|
||||
int Al = cinfo->Al;
|
||||
register int s, k, r;
|
||||
unsigned int EOBRUN;
|
||||
JBLOCKROW block;
|
||||
BITREAD_STATE_VARS;
|
||||
d_derived_tbl * tbl;
|
||||
|
||||
/* Process restart marker if needed; may have to suspend */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0)
|
||||
if (! process_restart(cinfo))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Load up working state.
|
||||
* We can avoid loading/saving bitread state if in an EOB run.
|
||||
*/
|
||||
EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we care about */
|
||||
|
||||
/* There is always only one block per MCU */
|
||||
|
||||
if (EOBRUN > 0) /* if it's a band of zeroes... */
|
||||
EOBRUN--; /* ...process it now (we do nothing) */
|
||||
else {
|
||||
BITREAD_LOAD_STATE(cinfo,entropy->bitstate);
|
||||
block = MCU_data[0];
|
||||
tbl = entropy->ac_derived_tbl;
|
||||
|
||||
for (k = cinfo->Ss; k <= Se; k++) {
|
||||
HUFF_DECODE(s, br_state, tbl, return FALSE, label2);
|
||||
r = s >> 4;
|
||||
s &= 15;
|
||||
if (s) {
|
||||
k += r;
|
||||
CHECK_BIT_BUFFER(br_state, s, return FALSE);
|
||||
r = GET_BITS(s);
|
||||
s = HUFF_EXTEND(r, s);
|
||||
/* Scale and output coefficient in natural (dezigzagged) order */
|
||||
(*block)[jpeg_natural_order[k]] = (JCOEF) (s << Al);
|
||||
} else {
|
||||
if (r == 15) { /* ZRL */
|
||||
k += 15; /* skip 15 zeroes in band */
|
||||
} else { /* EOBr, run length is 2^r + appended bits */
|
||||
EOBRUN = 1 << r;
|
||||
if (r) { /* EOBr, r > 0 */
|
||||
CHECK_BIT_BUFFER(br_state, r, return FALSE);
|
||||
r = GET_BITS(r);
|
||||
EOBRUN += r;
|
||||
}
|
||||
EOBRUN--; /* this band is processed at this moment */
|
||||
break; /* force end-of-band */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BITREAD_SAVE_STATE(cinfo,entropy->bitstate);
|
||||
}
|
||||
|
||||
/* Completed MCU, so update state */
|
||||
entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we care about */
|
||||
|
||||
/* Account for restart interval (no-op if not using restarts) */
|
||||
entropy->restarts_to_go--;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MCU decoding for DC successive approximation refinement scan.
|
||||
* Note: we assume such scans can be multi-component, although the spec
|
||||
* is not very clear on the point.
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */
|
||||
int blkn;
|
||||
JBLOCKROW block;
|
||||
BITREAD_STATE_VARS;
|
||||
|
||||
/* Process restart marker if needed; may have to suspend */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0)
|
||||
if (! process_restart(cinfo))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Load up working state */
|
||||
BITREAD_LOAD_STATE(cinfo,entropy->bitstate);
|
||||
|
||||
/* Outer loop handles each block in the MCU */
|
||||
|
||||
for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) {
|
||||
block = MCU_data[blkn];
|
||||
|
||||
/* Encoded data is simply the next bit of the two's-complement DC value */
|
||||
CHECK_BIT_BUFFER(br_state, 1, return FALSE);
|
||||
if (GET_BITS(1))
|
||||
(*block)[0] |= p1;
|
||||
/* Note: since we use |=, repeating the assignment later is safe */
|
||||
}
|
||||
|
||||
/* Completed MCU, so update state */
|
||||
BITREAD_SAVE_STATE(cinfo,entropy->bitstate);
|
||||
|
||||
/* Account for restart interval (no-op if not using restarts) */
|
||||
entropy->restarts_to_go--;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MCU decoding for AC successive approximation refinement scan.
|
||||
*/
|
||||
|
||||
METHODDEF boolean
|
||||
decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
|
||||
{
|
||||
phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy;
|
||||
int Se = cinfo->Se;
|
||||
int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */
|
||||
int m1 = (-1) << cinfo->Al; /* -1 in the bit position being coded */
|
||||
register int s, k, r;
|
||||
unsigned int EOBRUN;
|
||||
JBLOCKROW block;
|
||||
JCOEFPTR thiscoef;
|
||||
BITREAD_STATE_VARS;
|
||||
d_derived_tbl * tbl;
|
||||
int num_newnz;
|
||||
int newnz_pos[DCTSIZE2];
|
||||
|
||||
/* Process restart marker if needed; may have to suspend */
|
||||
if (cinfo->restart_interval) {
|
||||
if (entropy->restarts_to_go == 0)
|
||||
if (! process_restart(cinfo))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Load up working state */
|
||||
BITREAD_LOAD_STATE(cinfo,entropy->bitstate);
|
||||
EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we care about */
|
||||
|
||||
/* There is always only one block per MCU */
|
||||
block = MCU_data[0];
|
||||
tbl = entropy->ac_derived_tbl;
|
||||
|
||||
/* If we are forced to suspend, we must undo the assignments to any newly
|
||||
* nonzero coefficients in the block, because otherwise we'd get confused
|
||||
* next time about which coefficients were already nonzero.
|
||||
* But we need not undo addition of bits to already-nonzero coefficients;
|
||||
* instead, we can test the current bit position to see if we already did it.
|
||||
*/
|
||||
num_newnz = 0;
|
||||
|
||||
/* initialize coefficient loop counter to start of band */
|
||||
k = cinfo->Ss;
|
||||
|
||||
if (EOBRUN == 0) {
|
||||
for (; k <= Se; k++) {
|
||||
HUFF_DECODE(s, br_state, tbl, goto undoit, label3);
|
||||
r = s >> 4;
|
||||
s &= 15;
|
||||
if (s) {
|
||||
if (s != 1) /* size of new coef should always be 1 */
|
||||
WARNMS(cinfo, JWRN_HUFF_BAD_CODE);
|
||||
CHECK_BIT_BUFFER(br_state, 1, goto undoit);
|
||||
if (GET_BITS(1))
|
||||
s = p1; /* newly nonzero coef is positive */
|
||||
else
|
||||
s = m1; /* newly nonzero coef is negative */
|
||||
} else {
|
||||
if (r != 15) {
|
||||
EOBRUN = 1 << r; /* EOBr, run length is 2^r + appended bits */
|
||||
if (r) {
|
||||
CHECK_BIT_BUFFER(br_state, r, goto undoit);
|
||||
r = GET_BITS(r);
|
||||
EOBRUN += r;
|
||||
}
|
||||
break; /* rest of block is handled by EOB logic */
|
||||
}
|
||||
/* note s = 0 for processing ZRL */
|
||||
}
|
||||
/* Advance over already-nonzero coefs and r still-zero coefs,
|
||||
* appending correction bits to the nonzeroes. A correction bit is 1
|
||||
* if the absolute value of the coefficient must be increased.
|
||||
*/
|
||||
do {
|
||||
thiscoef = *block + jpeg_natural_order[k];
|
||||
if (*thiscoef != 0) {
|
||||
CHECK_BIT_BUFFER(br_state, 1, goto undoit);
|
||||
if (GET_BITS(1)) {
|
||||
if ((*thiscoef & p1) == 0) { /* do nothing if already changed it */
|
||||
if (*thiscoef >= 0)
|
||||
*thiscoef += p1;
|
||||
else
|
||||
*thiscoef += m1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (--r < 0)
|
||||
break; /* reached target zero coefficient */
|
||||
}
|
||||
k++;
|
||||
} while (k <= Se);
|
||||
if (s) {
|
||||
int pos = jpeg_natural_order[k];
|
||||
/* Output newly nonzero coefficient */
|
||||
(*block)[pos] = (JCOEF) s;
|
||||
/* Remember its position in case we have to suspend */
|
||||
newnz_pos[num_newnz++] = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (EOBRUN > 0) {
|
||||
/* Scan any remaining coefficient positions after the end-of-band
|
||||
* (the last newly nonzero coefficient, if any). Append a correction
|
||||
* bit to each already-nonzero coefficient. A correction bit is 1
|
||||
* if the absolute value of the coefficient must be increased.
|
||||
*/
|
||||
for (; k <= Se; k++) {
|
||||
thiscoef = *block + jpeg_natural_order[k];
|
||||
if (*thiscoef != 0) {
|
||||
CHECK_BIT_BUFFER(br_state, 1, goto undoit);
|
||||
if (GET_BITS(1)) {
|
||||
if ((*thiscoef & p1) == 0) { /* do nothing if already changed it */
|
||||
if (*thiscoef >= 0)
|
||||
*thiscoef += p1;
|
||||
else
|
||||
*thiscoef += m1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Count one block completed in EOB run */
|
||||
EOBRUN--;
|
||||
}
|
||||
|
||||
/* Completed MCU, so update state */
|
||||
BITREAD_SAVE_STATE(cinfo,entropy->bitstate);
|
||||
entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we care about */
|
||||
|
||||
/* Account for restart interval (no-op if not using restarts) */
|
||||
entropy->restarts_to_go--;
|
||||
|
||||
return TRUE;
|
||||
|
||||
undoit:
|
||||
/* Re-zero any output coefficients that we made newly nonzero */
|
||||
while (num_newnz > 0)
|
||||
(*block)[newnz_pos[--num_newnz]] = 0;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Module initialization routine for progressive Huffman entropy decoding.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_phuff_decoder (j_decompress_ptr cinfo)
|
||||
{
|
||||
phuff_entropy_ptr entropy;
|
||||
int *coef_bit_ptr;
|
||||
int ci, i;
|
||||
|
||||
entropy = (phuff_entropy_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(phuff_entropy_decoder));
|
||||
cinfo->entropy = (struct jpeg_entropy_decoder *) entropy;
|
||||
entropy->pub.start_pass = start_pass_phuff_decoder;
|
||||
|
||||
/* Mark derived tables unallocated */
|
||||
for (i = 0; i < NUM_HUFF_TBLS; i++) {
|
||||
entropy->derived_tbls[i] = NULL;
|
||||
}
|
||||
|
||||
/* Create progression status table */
|
||||
cinfo->coef_bits = (int (*)[DCTSIZE2])
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
cinfo->num_components*DCTSIZE2*SIZEOF(int));
|
||||
coef_bit_ptr = & cinfo->coef_bits[0][0];
|
||||
for (ci = 0; ci < cinfo->num_components; ci++)
|
||||
for (i = 0; i < DCTSIZE2; i++)
|
||||
*coef_bit_ptr++ = -1;
|
||||
}
|
||||
|
||||
#endif /* D_PROGRESSIVE_SUPPORTED */
|
||||
290
neo/renderer/jpeg-6/jdpostct.c
Normal file
290
neo/renderer/jpeg-6/jdpostct.c
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* jdpostct.c
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains the decompression postprocessing controller.
|
||||
* This controller manages the upsampling, color conversion, and color
|
||||
* quantization/reduction steps; specifically, it controls the buffering
|
||||
* between upsample/color conversion and color quantization/reduction.
|
||||
*
|
||||
* If no color quantization/reduction is required, then this module has no
|
||||
* work to do, and it just hands off to the upsample/color conversion code.
|
||||
* An integrated upsample/convert/quantize process would replace this module
|
||||
* entirely.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Private buffer controller object */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_d_post_controller pub; /* public fields */
|
||||
|
||||
/* Color quantization source buffer: this holds output data from
|
||||
* the upsample/color conversion step to be passed to the quantizer.
|
||||
* For two-pass color quantization, we need a full-image buffer;
|
||||
* for one-pass operation, a strip buffer is sufficient.
|
||||
*/
|
||||
jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */
|
||||
JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */
|
||||
JDIMENSION strip_height; /* buffer size in rows */
|
||||
/* for two-pass mode only: */
|
||||
JDIMENSION starting_row; /* row # of first row in current strip */
|
||||
JDIMENSION next_row; /* index of next row to fill/empty in strip */
|
||||
} my_post_controller;
|
||||
|
||||
typedef my_post_controller * my_post_ptr;
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
METHODDEF void post_process_1pass
|
||||
JPP((j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
|
||||
JDIMENSION in_row_groups_avail,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail));
|
||||
#ifdef QUANT_2PASS_SUPPORTED
|
||||
METHODDEF void post_process_prepass
|
||||
JPP((j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
|
||||
JDIMENSION in_row_groups_avail,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail));
|
||||
METHODDEF void post_process_2pass
|
||||
JPP((j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
|
||||
JDIMENSION in_row_groups_avail,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail));
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for a processing pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)
|
||||
{
|
||||
my_post_ptr post = (my_post_ptr) cinfo->post;
|
||||
|
||||
switch (pass_mode) {
|
||||
case JBUF_PASS_THRU:
|
||||
if (cinfo->quantize_colors) {
|
||||
/* Single-pass processing with color quantization. */
|
||||
post->pub.post_process_data = post_process_1pass;
|
||||
/* We could be doing buffered-image output before starting a 2-pass
|
||||
* color quantization; in that case, jinit_d_post_controller did not
|
||||
* allocate a strip buffer. Use the virtual-array buffer as workspace.
|
||||
*/
|
||||
if (post->buffer == NULL) {
|
||||
post->buffer = (*cinfo->mem->access_virt_sarray)
|
||||
((j_common_ptr) cinfo, post->whole_image,
|
||||
(JDIMENSION) 0, post->strip_height, TRUE);
|
||||
}
|
||||
} else {
|
||||
/* For single-pass processing without color quantization,
|
||||
* I have no work to do; just call the upsampler directly.
|
||||
*/
|
||||
post->pub.post_process_data = cinfo->upsample->upsample;
|
||||
}
|
||||
break;
|
||||
#ifdef QUANT_2PASS_SUPPORTED
|
||||
case JBUF_SAVE_AND_PASS:
|
||||
/* First pass of 2-pass quantization */
|
||||
if (post->whole_image == NULL)
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
post->pub.post_process_data = post_process_prepass;
|
||||
break;
|
||||
case JBUF_CRANK_DEST:
|
||||
/* Second pass of 2-pass quantization */
|
||||
if (post->whole_image == NULL)
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
post->pub.post_process_data = post_process_2pass;
|
||||
break;
|
||||
#endif /* QUANT_2PASS_SUPPORTED */
|
||||
default:
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
break;
|
||||
}
|
||||
post->starting_row = post->next_row = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process some data in the one-pass (strip buffer) case.
|
||||
* This is used for color precision reduction as well as one-pass quantization.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
post_process_1pass (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
|
||||
JDIMENSION in_row_groups_avail,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail)
|
||||
{
|
||||
my_post_ptr post = (my_post_ptr) cinfo->post;
|
||||
JDIMENSION num_rows, max_rows;
|
||||
|
||||
/* Fill the buffer, but not more than what we can dump out in one go. */
|
||||
/* Note we rely on the upsampler to detect bottom of image. */
|
||||
max_rows = out_rows_avail - *out_row_ctr;
|
||||
if (max_rows > post->strip_height)
|
||||
max_rows = post->strip_height;
|
||||
num_rows = 0;
|
||||
(*cinfo->upsample->upsample) (cinfo,
|
||||
input_buf, in_row_group_ctr, in_row_groups_avail,
|
||||
post->buffer, &num_rows, max_rows);
|
||||
/* Quantize and emit data. */
|
||||
(*cinfo->cquantize->color_quantize) (cinfo,
|
||||
post->buffer, output_buf + *out_row_ctr, (int) num_rows);
|
||||
*out_row_ctr += num_rows;
|
||||
}
|
||||
|
||||
|
||||
#ifdef QUANT_2PASS_SUPPORTED
|
||||
|
||||
/*
|
||||
* Process some data in the first pass of 2-pass quantization.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
post_process_prepass (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
|
||||
JDIMENSION in_row_groups_avail,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail)
|
||||
{
|
||||
my_post_ptr post = (my_post_ptr) cinfo->post;
|
||||
JDIMENSION old_next_row, num_rows;
|
||||
|
||||
/* Reposition virtual buffer if at start of strip. */
|
||||
if (post->next_row == 0) {
|
||||
post->buffer = (*cinfo->mem->access_virt_sarray)
|
||||
((j_common_ptr) cinfo, post->whole_image,
|
||||
post->starting_row, post->strip_height, TRUE);
|
||||
}
|
||||
|
||||
/* Upsample some data (up to a strip height's worth). */
|
||||
old_next_row = post->next_row;
|
||||
(*cinfo->upsample->upsample) (cinfo,
|
||||
input_buf, in_row_group_ctr, in_row_groups_avail,
|
||||
post->buffer, &post->next_row, post->strip_height);
|
||||
|
||||
/* Allow quantizer to scan new data. No data is emitted, */
|
||||
/* but we advance out_row_ctr so outer loop can tell when we're done. */
|
||||
if (post->next_row > old_next_row) {
|
||||
num_rows = post->next_row - old_next_row;
|
||||
(*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row,
|
||||
(JSAMPARRAY) NULL, (int) num_rows);
|
||||
*out_row_ctr += num_rows;
|
||||
}
|
||||
|
||||
/* Advance if we filled the strip. */
|
||||
if (post->next_row >= post->strip_height) {
|
||||
post->starting_row += post->strip_height;
|
||||
post->next_row = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process some data in the second pass of 2-pass quantization.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
post_process_2pass (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
|
||||
JDIMENSION in_row_groups_avail,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail)
|
||||
{
|
||||
my_post_ptr post = (my_post_ptr) cinfo->post;
|
||||
JDIMENSION num_rows, max_rows;
|
||||
|
||||
/* Reposition virtual buffer if at start of strip. */
|
||||
if (post->next_row == 0) {
|
||||
post->buffer = (*cinfo->mem->access_virt_sarray)
|
||||
((j_common_ptr) cinfo, post->whole_image,
|
||||
post->starting_row, post->strip_height, FALSE);
|
||||
}
|
||||
|
||||
/* Determine number of rows to emit. */
|
||||
num_rows = post->strip_height - post->next_row; /* available in strip */
|
||||
max_rows = out_rows_avail - *out_row_ctr; /* available in output area */
|
||||
if (num_rows > max_rows)
|
||||
num_rows = max_rows;
|
||||
/* We have to check bottom of image here, can't depend on upsampler. */
|
||||
max_rows = cinfo->output_height - post->starting_row;
|
||||
if (num_rows > max_rows)
|
||||
num_rows = max_rows;
|
||||
|
||||
/* Quantize and emit data. */
|
||||
(*cinfo->cquantize->color_quantize) (cinfo,
|
||||
post->buffer + post->next_row, output_buf + *out_row_ctr,
|
||||
(int) num_rows);
|
||||
*out_row_ctr += num_rows;
|
||||
|
||||
/* Advance if we filled the strip. */
|
||||
post->next_row += num_rows;
|
||||
if (post->next_row >= post->strip_height) {
|
||||
post->starting_row += post->strip_height;
|
||||
post->next_row = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* QUANT_2PASS_SUPPORTED */
|
||||
|
||||
|
||||
/*
|
||||
* Initialize postprocessing controller.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer)
|
||||
{
|
||||
my_post_ptr post;
|
||||
|
||||
post = (my_post_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_post_controller));
|
||||
cinfo->post = (struct jpeg_d_post_controller *) post;
|
||||
post->pub.start_pass = start_pass_dpost;
|
||||
post->whole_image = NULL; /* flag for no virtual arrays */
|
||||
post->buffer = NULL; /* flag for no strip buffer */
|
||||
|
||||
/* Create the quantization buffer, if needed */
|
||||
if (cinfo->quantize_colors) {
|
||||
/* The buffer strip height is max_v_samp_factor, which is typically
|
||||
* an efficient number of rows for upsampling to return.
|
||||
* (In the presence of output rescaling, we might want to be smarter?)
|
||||
*/
|
||||
post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor;
|
||||
if (need_full_buffer) {
|
||||
/* Two-pass color quantization: need full-image storage. */
|
||||
/* We round up the number of rows to a multiple of the strip height. */
|
||||
#ifdef QUANT_2PASS_SUPPORTED
|
||||
post->whole_image = (*cinfo->mem->request_virt_sarray)
|
||||
((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
|
||||
cinfo->output_width * cinfo->out_color_components,
|
||||
(JDIMENSION) jround_up((long) cinfo->output_height,
|
||||
(long) post->strip_height),
|
||||
post->strip_height);
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
|
||||
#endif /* QUANT_2PASS_SUPPORTED */
|
||||
} else {
|
||||
/* One-pass color quantization: just make a strip buffer. */
|
||||
post->buffer = (*cinfo->mem->alloc_sarray)
|
||||
((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
cinfo->output_width * cinfo->out_color_components,
|
||||
post->strip_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
478
neo/renderer/jpeg-6/jdsample.c
Normal file
478
neo/renderer/jpeg-6/jdsample.c
Normal file
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* jdsample.c
|
||||
*
|
||||
* Copyright (C) 1991-1994, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains upsampling routines.
|
||||
*
|
||||
* Upsampling input data is counted in "row groups". A row group
|
||||
* is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size)
|
||||
* sample rows of each component. Upsampling will normally produce
|
||||
* max_v_samp_factor pixel rows from each row group (but this could vary
|
||||
* if the upsampler is applying a scale factor of its own).
|
||||
*
|
||||
* An excellent reference for image resampling is
|
||||
* Digital Image Warping, George Wolberg, 1990.
|
||||
* Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Pointer to routine to upsample a single component */
|
||||
typedef JMETHOD(void, upsample1_ptr,
|
||||
(j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr));
|
||||
|
||||
/* Private subobject */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_upsampler pub; /* public fields */
|
||||
|
||||
/* Color conversion buffer. When using separate upsampling and color
|
||||
* conversion steps, this buffer holds one upsampled row group until it
|
||||
* has been color converted and output.
|
||||
* Note: we do not allocate any storage for component(s) which are full-size,
|
||||
* ie do not need rescaling. The corresponding entry of color_buf[] is
|
||||
* simply set to point to the input data array, thereby avoiding copying.
|
||||
*/
|
||||
JSAMPARRAY color_buf[MAX_COMPONENTS];
|
||||
|
||||
/* Per-component upsampling method pointers */
|
||||
upsample1_ptr methods[MAX_COMPONENTS];
|
||||
|
||||
int next_row_out; /* counts rows emitted from color_buf */
|
||||
JDIMENSION rows_to_go; /* counts rows remaining in image */
|
||||
|
||||
/* Height of an input row group for each component. */
|
||||
int rowgroup_height[MAX_COMPONENTS];
|
||||
|
||||
/* These arrays save pixel expansion factors so that int_expand need not
|
||||
* recompute them each time. They are unused for other upsampling methods.
|
||||
*/
|
||||
UINT8 h_expand[MAX_COMPONENTS];
|
||||
UINT8 v_expand[MAX_COMPONENTS];
|
||||
} my_upsampler;
|
||||
|
||||
typedef my_upsampler * my_upsample_ptr;
|
||||
|
||||
|
||||
/*
|
||||
* Initialize for an upsampling pass.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
start_pass_upsample (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
|
||||
|
||||
/* Mark the conversion buffer empty */
|
||||
upsample->next_row_out = cinfo->max_v_samp_factor;
|
||||
/* Initialize total-height counter for detecting bottom of image */
|
||||
upsample->rows_to_go = cinfo->output_height;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Control routine to do upsampling (and color conversion).
|
||||
*
|
||||
* In this version we upsample each component independently.
|
||||
* We upsample one row group into the conversion buffer, then apply
|
||||
* color conversion a row at a time.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
sep_upsample (j_decompress_ptr cinfo,
|
||||
JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
|
||||
JDIMENSION in_row_groups_avail,
|
||||
JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
|
||||
JDIMENSION out_rows_avail)
|
||||
{
|
||||
my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
|
||||
int ci;
|
||||
jpeg_component_info * compptr;
|
||||
JDIMENSION num_rows;
|
||||
|
||||
/* Fill the conversion buffer, if it's empty */
|
||||
if (upsample->next_row_out >= cinfo->max_v_samp_factor) {
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* Invoke per-component upsample method. Notice we pass a POINTER
|
||||
* to color_buf[ci], so that fullsize_upsample can change it.
|
||||
*/
|
||||
(*upsample->methods[ci]) (cinfo, compptr,
|
||||
input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]),
|
||||
upsample->color_buf + ci);
|
||||
}
|
||||
upsample->next_row_out = 0;
|
||||
}
|
||||
|
||||
/* Color-convert and emit rows */
|
||||
|
||||
/* How many we have in the buffer: */
|
||||
num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out);
|
||||
/* Not more than the distance to the end of the image. Need this test
|
||||
* in case the image height is not a multiple of max_v_samp_factor:
|
||||
*/
|
||||
if (num_rows > upsample->rows_to_go)
|
||||
num_rows = upsample->rows_to_go;
|
||||
/* And not more than what the client can accept: */
|
||||
out_rows_avail -= *out_row_ctr;
|
||||
if (num_rows > out_rows_avail)
|
||||
num_rows = out_rows_avail;
|
||||
|
||||
(*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf,
|
||||
(JDIMENSION) upsample->next_row_out,
|
||||
output_buf + *out_row_ctr,
|
||||
(int) num_rows);
|
||||
|
||||
/* Adjust counts */
|
||||
*out_row_ctr += num_rows;
|
||||
upsample->rows_to_go -= num_rows;
|
||||
upsample->next_row_out += num_rows;
|
||||
/* When the buffer is emptied, declare this input row group consumed */
|
||||
if (upsample->next_row_out >= cinfo->max_v_samp_factor)
|
||||
(*in_row_group_ctr)++;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* These are the routines invoked by sep_upsample to upsample pixel values
|
||||
* of a single component. One row group is processed per call.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* For full-size components, we just make color_buf[ci] point at the
|
||||
* input buffer, and thus avoid copying any data. Note that this is
|
||||
* safe only because sep_upsample doesn't declare the input row group
|
||||
* "consumed" until we are done color converting and emitting it.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
fullsize_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
|
||||
{
|
||||
*output_data_ptr = input_data;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is a no-op version used for "uninteresting" components.
|
||||
* These components will not be referenced by color conversion.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
noop_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
|
||||
{
|
||||
*output_data_ptr = NULL; /* safety check */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This version handles any integral sampling ratios.
|
||||
* This is not used for typical JPEG files, so it need not be fast.
|
||||
* Nor, for that matter, is it particularly accurate: the algorithm is
|
||||
* simple replication of the input pixel onto the corresponding output
|
||||
* pixels. The hi-falutin sampling literature refers to this as a
|
||||
* "box filter". A box filter tends to introduce visible artifacts,
|
||||
* so if you are actually going to use 3:1 or 4:1 sampling ratios
|
||||
* you would be well advised to improve this code.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
|
||||
{
|
||||
my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample;
|
||||
JSAMPARRAY output_data = *output_data_ptr;
|
||||
register JSAMPROW inptr, outptr;
|
||||
register JSAMPLE invalue;
|
||||
register int h;
|
||||
JSAMPROW outend;
|
||||
int h_expand, v_expand;
|
||||
int inrow, outrow;
|
||||
|
||||
h_expand = upsample->h_expand[compptr->component_index];
|
||||
v_expand = upsample->v_expand[compptr->component_index];
|
||||
|
||||
inrow = outrow = 0;
|
||||
while (outrow < cinfo->max_v_samp_factor) {
|
||||
/* Generate one output row with proper horizontal expansion */
|
||||
inptr = input_data[inrow];
|
||||
outptr = output_data[outrow];
|
||||
outend = outptr + cinfo->output_width;
|
||||
while (outptr < outend) {
|
||||
invalue = *inptr++; /* don't need GETJSAMPLE() here */
|
||||
for (h = h_expand; h > 0; h--) {
|
||||
*outptr++ = invalue;
|
||||
}
|
||||
}
|
||||
/* Generate any additional output rows by duplicating the first one */
|
||||
if (v_expand > 1) {
|
||||
jcopy_sample_rows(output_data, outrow, output_data, outrow+1,
|
||||
v_expand-1, cinfo->output_width);
|
||||
}
|
||||
inrow++;
|
||||
outrow += v_expand;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fast processing for the common case of 2:1 horizontal and 1:1 vertical.
|
||||
* It's still a box filter.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
h2v1_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
|
||||
{
|
||||
JSAMPARRAY output_data = *output_data_ptr;
|
||||
register JSAMPROW inptr, outptr;
|
||||
register JSAMPLE invalue;
|
||||
JSAMPROW outend;
|
||||
int inrow;
|
||||
|
||||
for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) {
|
||||
inptr = input_data[inrow];
|
||||
outptr = output_data[inrow];
|
||||
outend = outptr + cinfo->output_width;
|
||||
while (outptr < outend) {
|
||||
invalue = *inptr++; /* don't need GETJSAMPLE() here */
|
||||
*outptr++ = invalue;
|
||||
*outptr++ = invalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fast processing for the common case of 2:1 horizontal and 2:1 vertical.
|
||||
* It's still a box filter.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
h2v2_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
|
||||
{
|
||||
JSAMPARRAY output_data = *output_data_ptr;
|
||||
register JSAMPROW inptr, outptr;
|
||||
register JSAMPLE invalue;
|
||||
JSAMPROW outend;
|
||||
int inrow, outrow;
|
||||
|
||||
inrow = outrow = 0;
|
||||
while (outrow < cinfo->max_v_samp_factor) {
|
||||
inptr = input_data[inrow];
|
||||
outptr = output_data[outrow];
|
||||
outend = outptr + cinfo->output_width;
|
||||
while (outptr < outend) {
|
||||
invalue = *inptr++; /* don't need GETJSAMPLE() here */
|
||||
*outptr++ = invalue;
|
||||
*outptr++ = invalue;
|
||||
}
|
||||
jcopy_sample_rows(output_data, outrow, output_data, outrow+1,
|
||||
1, cinfo->output_width);
|
||||
inrow++;
|
||||
outrow += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fancy processing for the common case of 2:1 horizontal and 1:1 vertical.
|
||||
*
|
||||
* The upsampling algorithm is linear interpolation between pixel centers,
|
||||
* also known as a "triangle filter". This is a good compromise between
|
||||
* speed and visual quality. The centers of the output pixels are 1/4 and 3/4
|
||||
* of the way between input pixel centers.
|
||||
*
|
||||
* A note about the "bias" calculations: when rounding fractional values to
|
||||
* integer, we do not want to always round 0.5 up to the next integer.
|
||||
* If we did that, we'd introduce a noticeable bias towards larger values.
|
||||
* Instead, this code is arranged so that 0.5 will be rounded up or down at
|
||||
* alternate pixel locations (a simple ordered dither pattern).
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
h2v1_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
|
||||
{
|
||||
JSAMPARRAY output_data = *output_data_ptr;
|
||||
register JSAMPROW inptr, outptr;
|
||||
register int invalue;
|
||||
register JDIMENSION colctr;
|
||||
int inrow;
|
||||
|
||||
for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) {
|
||||
inptr = input_data[inrow];
|
||||
outptr = output_data[inrow];
|
||||
/* Special case for first column */
|
||||
invalue = GETJSAMPLE(*inptr++);
|
||||
*outptr++ = (JSAMPLE) invalue;
|
||||
*outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(*inptr) + 2) >> 2);
|
||||
|
||||
for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) {
|
||||
/* General case: 3/4 * nearer pixel + 1/4 * further pixel */
|
||||
invalue = GETJSAMPLE(*inptr++) * 3;
|
||||
*outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(inptr[-2]) + 1) >> 2);
|
||||
*outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(*inptr) + 2) >> 2);
|
||||
}
|
||||
|
||||
/* Special case for last column */
|
||||
invalue = GETJSAMPLE(*inptr);
|
||||
*outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(inptr[-1]) + 1) >> 2);
|
||||
*outptr++ = (JSAMPLE) invalue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fancy processing for the common case of 2:1 horizontal and 2:1 vertical.
|
||||
* Again a triangle filter; see comments for h2v1 case, above.
|
||||
*
|
||||
* It is OK for us to reference the adjacent input rows because we demanded
|
||||
* context from the main buffer controller (see initialization code).
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr,
|
||||
JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)
|
||||
{
|
||||
JSAMPARRAY output_data = *output_data_ptr;
|
||||
register JSAMPROW inptr0, inptr1, outptr;
|
||||
#if BITS_IN_JSAMPLE == 8
|
||||
register int thiscolsum, lastcolsum, nextcolsum;
|
||||
#else
|
||||
register INT32 thiscolsum, lastcolsum, nextcolsum;
|
||||
#endif
|
||||
register JDIMENSION colctr;
|
||||
int inrow, outrow, v;
|
||||
|
||||
inrow = outrow = 0;
|
||||
while (outrow < cinfo->max_v_samp_factor) {
|
||||
for (v = 0; v < 2; v++) {
|
||||
/* inptr0 points to nearest input row, inptr1 points to next nearest */
|
||||
inptr0 = input_data[inrow];
|
||||
if (v == 0) /* next nearest is row above */
|
||||
inptr1 = input_data[inrow-1];
|
||||
else /* next nearest is row below */
|
||||
inptr1 = input_data[inrow+1];
|
||||
outptr = output_data[outrow++];
|
||||
|
||||
/* Special case for first column */
|
||||
thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++);
|
||||
nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++);
|
||||
*outptr++ = (JSAMPLE) ((thiscolsum * 4 + 8) >> 4);
|
||||
*outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4);
|
||||
lastcolsum = thiscolsum; thiscolsum = nextcolsum;
|
||||
|
||||
for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) {
|
||||
/* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */
|
||||
/* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */
|
||||
nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++);
|
||||
*outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4);
|
||||
*outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4);
|
||||
lastcolsum = thiscolsum; thiscolsum = nextcolsum;
|
||||
}
|
||||
|
||||
/* Special case for last column */
|
||||
*outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4);
|
||||
*outptr++ = (JSAMPLE) ((thiscolsum * 4 + 7) >> 4);
|
||||
}
|
||||
inrow++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Module initialization routine for upsampling.
|
||||
*/
|
||||
|
||||
GLOBAL void
|
||||
jinit_upsampler (j_decompress_ptr cinfo)
|
||||
{
|
||||
my_upsample_ptr upsample;
|
||||
int ci;
|
||||
jpeg_component_info * compptr;
|
||||
boolean need_buffer, do_fancy;
|
||||
int h_in_group, v_in_group, h_out_group, v_out_group;
|
||||
|
||||
upsample = (my_upsample_ptr)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
SIZEOF(my_upsampler));
|
||||
cinfo->upsample = (struct jpeg_upsampler *) upsample;
|
||||
upsample->pub.start_pass = start_pass_upsample;
|
||||
upsample->pub.upsample = sep_upsample;
|
||||
upsample->pub.need_context_rows = FALSE; /* until we find out differently */
|
||||
|
||||
if (cinfo->CCIR601_sampling) /* this isn't supported */
|
||||
ERREXIT(cinfo, JERR_CCIR601_NOTIMPL);
|
||||
|
||||
/* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1,
|
||||
* so don't ask for it.
|
||||
*/
|
||||
do_fancy = cinfo->do_fancy_upsampling && cinfo->min_DCT_scaled_size > 1;
|
||||
|
||||
/* Verify we can handle the sampling factors, select per-component methods,
|
||||
* and create storage as needed.
|
||||
*/
|
||||
for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
|
||||
ci++, compptr++) {
|
||||
/* Compute size of an "input group" after IDCT scaling. This many samples
|
||||
* are to be converted to max_h_samp_factor * max_v_samp_factor pixels.
|
||||
*/
|
||||
h_in_group = (compptr->h_samp_factor * compptr->DCT_scaled_size) /
|
||||
cinfo->min_DCT_scaled_size;
|
||||
v_in_group = (compptr->v_samp_factor * compptr->DCT_scaled_size) /
|
||||
cinfo->min_DCT_scaled_size;
|
||||
h_out_group = cinfo->max_h_samp_factor;
|
||||
v_out_group = cinfo->max_v_samp_factor;
|
||||
upsample->rowgroup_height[ci] = v_in_group; /* save for use later */
|
||||
need_buffer = TRUE;
|
||||
if (! compptr->component_needed) {
|
||||
/* Don't bother to upsample an uninteresting component. */
|
||||
upsample->methods[ci] = noop_upsample;
|
||||
need_buffer = FALSE;
|
||||
} else if (h_in_group == h_out_group && v_in_group == v_out_group) {
|
||||
/* Fullsize components can be processed without any work. */
|
||||
upsample->methods[ci] = fullsize_upsample;
|
||||
need_buffer = FALSE;
|
||||
} else if (h_in_group * 2 == h_out_group &&
|
||||
v_in_group == v_out_group) {
|
||||
/* Special cases for 2h1v upsampling */
|
||||
if (do_fancy && compptr->downsampled_width > 2)
|
||||
upsample->methods[ci] = h2v1_fancy_upsample;
|
||||
else
|
||||
upsample->methods[ci] = h2v1_upsample;
|
||||
} else if (h_in_group * 2 == h_out_group &&
|
||||
v_in_group * 2 == v_out_group) {
|
||||
/* Special cases for 2h2v upsampling */
|
||||
if (do_fancy && compptr->downsampled_width > 2) {
|
||||
upsample->methods[ci] = h2v2_fancy_upsample;
|
||||
upsample->pub.need_context_rows = TRUE;
|
||||
} else
|
||||
upsample->methods[ci] = h2v2_upsample;
|
||||
} else if ((h_out_group % h_in_group) == 0 &&
|
||||
(v_out_group % v_in_group) == 0) {
|
||||
/* Generic integral-factors upsampling method */
|
||||
upsample->methods[ci] = int_upsample;
|
||||
upsample->h_expand[ci] = (UINT8) (h_out_group / h_in_group);
|
||||
upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group);
|
||||
} else
|
||||
ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL);
|
||||
if (need_buffer) {
|
||||
upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray)
|
||||
((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||
(JDIMENSION) jround_up((long) cinfo->output_width,
|
||||
(long) cinfo->max_h_samp_factor),
|
||||
(JDIMENSION) cinfo->max_v_samp_factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
122
neo/renderer/jpeg-6/jdtrans.c
Normal file
122
neo/renderer/jpeg-6/jdtrans.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* jdtrans.c
|
||||
*
|
||||
* Copyright (C) 1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains library routines for transcoding decompression,
|
||||
* that is, reading raw DCT coefficient arrays from an input JPEG file.
|
||||
* The routines in jdapimin.c will also be needed by a transcoder.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
LOCAL void transdecode_master_selection JPP((j_decompress_ptr cinfo));
|
||||
|
||||
|
||||
/*
|
||||
* Read the coefficient arrays from a JPEG file.
|
||||
* jpeg_read_header must be completed before calling this.
|
||||
*
|
||||
* The entire image is read into a set of virtual coefficient-block arrays,
|
||||
* one per component. The return value is a pointer to the array of
|
||||
* virtual-array descriptors. These can be manipulated directly via the
|
||||
* JPEG memory manager, or handed off to jpeg_write_coefficients().
|
||||
* To release the memory occupied by the virtual arrays, call
|
||||
* jpeg_finish_decompress() when done with the data.
|
||||
*
|
||||
* Returns NULL if suspended. This case need be checked only if
|
||||
* a suspending data source is used.
|
||||
*/
|
||||
|
||||
GLOBAL jvirt_barray_ptr *
|
||||
jpeg_read_coefficients (j_decompress_ptr cinfo)
|
||||
{
|
||||
if (cinfo->global_state == DSTATE_READY) {
|
||||
/* First call: initialize active modules */
|
||||
transdecode_master_selection(cinfo);
|
||||
cinfo->global_state = DSTATE_RDCOEFS;
|
||||
} else if (cinfo->global_state != DSTATE_RDCOEFS)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
/* Absorb whole file into the coef buffer */
|
||||
for (;;) {
|
||||
int retcode;
|
||||
/* Call progress monitor hook if present */
|
||||
if (cinfo->progress != NULL)
|
||||
(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
|
||||
/* Absorb some more input */
|
||||
retcode = (*cinfo->inputctl->consume_input) (cinfo);
|
||||
if (retcode == JPEG_SUSPENDED)
|
||||
return NULL;
|
||||
if (retcode == JPEG_REACHED_EOI)
|
||||
break;
|
||||
/* Advance progress counter if appropriate */
|
||||
if (cinfo->progress != NULL &&
|
||||
(retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) {
|
||||
if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) {
|
||||
/* startup underestimated number of scans; ratchet up one scan */
|
||||
cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Set state so that jpeg_finish_decompress does the right thing */
|
||||
cinfo->global_state = DSTATE_STOPPING;
|
||||
return cinfo->coef->coef_arrays;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Master selection of decompression modules for transcoding.
|
||||
* This substitutes for jdmaster.c's initialization of the full decompressor.
|
||||
*/
|
||||
|
||||
LOCAL void
|
||||
transdecode_master_selection (j_decompress_ptr cinfo)
|
||||
{
|
||||
/* Entropy decoding: either Huffman or arithmetic coding. */
|
||||
if (cinfo->arith_code) {
|
||||
ERREXIT(cinfo, JERR_ARITH_NOTIMPL);
|
||||
} else {
|
||||
if (cinfo->progressive_mode) {
|
||||
#ifdef D_PROGRESSIVE_SUPPORTED
|
||||
jinit_phuff_decoder(cinfo);
|
||||
#else
|
||||
ERREXIT(cinfo, JERR_NOT_COMPILED);
|
||||
#endif
|
||||
} else
|
||||
jinit_huff_decoder(cinfo);
|
||||
}
|
||||
|
||||
/* Always get a full-image coefficient buffer. */
|
||||
jinit_d_coef_controller(cinfo, TRUE);
|
||||
|
||||
/* We can now tell the memory manager to allocate virtual arrays. */
|
||||
(*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo);
|
||||
|
||||
/* Initialize input side of decompressor to consume first scan. */
|
||||
(*cinfo->inputctl->start_input_pass) (cinfo);
|
||||
|
||||
/* Initialize progress monitoring. */
|
||||
if (cinfo->progress != NULL) {
|
||||
int nscans;
|
||||
/* Estimate number of scans to set pass_limit. */
|
||||
if (cinfo->progressive_mode) {
|
||||
/* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */
|
||||
nscans = 2 + 3 * cinfo->num_components;
|
||||
} else if (cinfo->inputctl->has_multiple_scans) {
|
||||
/* For a nonprogressive multiscan file, estimate 1 scan per component. */
|
||||
nscans = cinfo->num_components;
|
||||
} else {
|
||||
nscans = 1;
|
||||
}
|
||||
cinfo->progress->pass_counter = 0L;
|
||||
cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans;
|
||||
cinfo->progress->completed_passes = 0;
|
||||
cinfo->progress->total_passes = 1;
|
||||
}
|
||||
}
|
||||
234
neo/renderer/jpeg-6/jerror.c
Normal file
234
neo/renderer/jpeg-6/jerror.c
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* jerror.c
|
||||
*
|
||||
* Copyright (C) 1991-1994, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file contains simple error-reporting and trace-message routines.
|
||||
* These are suitable for Unix-like systems and others where writing to
|
||||
* stderr is the right thing to do. Many applications will want to replace
|
||||
* some or all of these routines.
|
||||
*
|
||||
* These routines are used by both the compression and decompression code.
|
||||
*/
|
||||
|
||||
/* this is not a core library module, so it doesn't define JPEG_INTERNALS */
|
||||
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jversion.h"
|
||||
#include "jerror.h"
|
||||
|
||||
extern jpg_Error( const char *fmt, ... );
|
||||
extern jpg_Printf( const char *fmt, ... );
|
||||
|
||||
#ifndef EXIT_FAILURE /* define exit() codes if not provided */
|
||||
#define EXIT_FAILURE 1
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Create the message string table.
|
||||
* We do this from the master message list in jerror.h by re-reading
|
||||
* jerror.h with a suitable definition for macro JMESSAGE.
|
||||
* The message table is made an external symbol just in case any applications
|
||||
* want to refer to it directly.
|
||||
*/
|
||||
|
||||
#ifdef NEED_SHORT_EXTERNAL_NAMES
|
||||
#define jpeg_std_message_table jMsgTable
|
||||
#endif
|
||||
|
||||
#define JMESSAGE(code,string) string ,
|
||||
|
||||
const char * const jpeg_std_message_table[] = {
|
||||
#include "jerror.h"
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Error exit handler: must not return to caller.
|
||||
*
|
||||
* Applications may override this if they want to get control back after
|
||||
* an error. Typically one would longjmp somewhere instead of exiting.
|
||||
* The setjmp buffer can be made a private field within an expanded error
|
||||
* handler object. Note that the info needed to generate an error message
|
||||
* is stored in the error object, so you can generate the message now or
|
||||
* later, at your convenience.
|
||||
* You should make sure that the JPEG object is cleaned up (with jpeg_abort
|
||||
* or jpeg_destroy) at some point.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
error_exit (j_common_ptr cinfo)
|
||||
{
|
||||
char buffer[JMSG_LENGTH_MAX];
|
||||
|
||||
/* Create the message */
|
||||
(*cinfo->err->format_message) (cinfo, buffer);
|
||||
|
||||
/* Let the memory manager delete any temp files before we die */
|
||||
jpeg_destroy(cinfo);
|
||||
|
||||
jpg_Error( "%s\n", buffer );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Actual output of an error or trace message.
|
||||
* Applications may override this method to send JPEG messages somewhere
|
||||
* other than stderr.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
output_message (j_common_ptr cinfo)
|
||||
{
|
||||
char buffer[JMSG_LENGTH_MAX];
|
||||
|
||||
/* Create the message */
|
||||
(*cinfo->err->format_message) (cinfo, buffer);
|
||||
|
||||
/* Send it to stderr, adding a newline */
|
||||
jpg_Printf( "%s\n", buffer );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Decide whether to emit a trace or warning message.
|
||||
* msg_level is one of:
|
||||
* -1: recoverable corrupt-data warning, may want to abort.
|
||||
* 0: important advisory messages (always display to user).
|
||||
* 1: first level of tracing detail.
|
||||
* 2,3,...: successively more detailed tracing messages.
|
||||
* An application might override this method if it wanted to abort on warnings
|
||||
* or change the policy about which messages to display.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
emit_message (j_common_ptr cinfo, int msg_level)
|
||||
{
|
||||
struct jpeg_error_mgr * err = cinfo->err;
|
||||
|
||||
if (msg_level < 0) {
|
||||
/* It's a warning message. Since corrupt files may generate many warnings,
|
||||
* the policy implemented here is to show only the first warning,
|
||||
* unless trace_level >= 3.
|
||||
*/
|
||||
if (err->num_warnings == 0 || err->trace_level >= 3)
|
||||
(*err->output_message) (cinfo);
|
||||
/* Always count warnings in num_warnings. */
|
||||
err->num_warnings++;
|
||||
} else {
|
||||
/* It's a trace message. Show it if trace_level >= msg_level. */
|
||||
if (err->trace_level >= msg_level)
|
||||
(*err->output_message) (cinfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Format a message string for the most recent JPEG error or message.
|
||||
* The message is stored into buffer, which should be at least JMSG_LENGTH_MAX
|
||||
* characters. Note that no '\n' character is added to the string.
|
||||
* Few applications should need to override this method.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
format_message (j_common_ptr cinfo, char * buffer)
|
||||
{
|
||||
struct jpeg_error_mgr * err = cinfo->err;
|
||||
int msg_code = err->msg_code;
|
||||
const char * msgtext = NULL;
|
||||
const char * msgptr;
|
||||
char ch;
|
||||
boolean isstring;
|
||||
|
||||
/* Look up message string in proper table */
|
||||
if (msg_code > 0 && msg_code <= err->last_jpeg_message) {
|
||||
msgtext = err->jpeg_message_table[msg_code];
|
||||
} else if (err->addon_message_table != NULL &&
|
||||
msg_code >= err->first_addon_message &&
|
||||
msg_code <= err->last_addon_message) {
|
||||
msgtext = err->addon_message_table[msg_code - err->first_addon_message];
|
||||
}
|
||||
|
||||
/* Defend against bogus message number */
|
||||
if (msgtext == NULL) {
|
||||
err->msg_parm.i[0] = msg_code;
|
||||
msgtext = err->jpeg_message_table[0];
|
||||
}
|
||||
|
||||
/* Check for string parameter, as indicated by %s in the message text */
|
||||
isstring = FALSE;
|
||||
msgptr = msgtext;
|
||||
while ((ch = *msgptr++) != '\0') {
|
||||
if (ch == '%') {
|
||||
if (*msgptr == 's') isstring = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Format the message into the passed buffer */
|
||||
if (isstring)
|
||||
sprintf(buffer, msgtext, err->msg_parm.s);
|
||||
else
|
||||
sprintf(buffer, msgtext,
|
||||
err->msg_parm.i[0], err->msg_parm.i[1],
|
||||
err->msg_parm.i[2], err->msg_parm.i[3],
|
||||
err->msg_parm.i[4], err->msg_parm.i[5],
|
||||
err->msg_parm.i[6], err->msg_parm.i[7]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reset error state variables at start of a new image.
|
||||
* This is called during compression startup to reset trace/error
|
||||
* processing to default state, without losing any application-specific
|
||||
* method pointers. An application might possibly want to override
|
||||
* this method if it has additional error processing state.
|
||||
*/
|
||||
|
||||
METHODDEF void
|
||||
reset_error_mgr (j_common_ptr cinfo)
|
||||
{
|
||||
cinfo->err->num_warnings = 0;
|
||||
/* trace_level is not reset since it is an application-supplied parameter */
|
||||
cinfo->err->msg_code = 0; /* may be useful as a flag for "no error" */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fill in the standard error-handling methods in a jpeg_error_mgr object.
|
||||
* Typical call is:
|
||||
* struct jpeg_compress_struct cinfo;
|
||||
* struct jpeg_error_mgr err;
|
||||
*
|
||||
* cinfo.err = jpeg_std_error(&err);
|
||||
* after which the application may override some of the methods.
|
||||
*/
|
||||
|
||||
GLOBAL struct jpeg_error_mgr *
|
||||
jpeg_std_error (struct jpeg_error_mgr * err)
|
||||
{
|
||||
err->error_exit = error_exit;
|
||||
err->emit_message = emit_message;
|
||||
err->output_message = output_message;
|
||||
err->format_message = format_message;
|
||||
err->reset_error_mgr = reset_error_mgr;
|
||||
|
||||
err->trace_level = 0; /* default = no tracing */
|
||||
err->num_warnings = 0; /* no warnings emitted yet */
|
||||
err->msg_code = 0; /* may be useful as a flag for "no error" */
|
||||
|
||||
/* Initialize message table pointers */
|
||||
err->jpeg_message_table = jpeg_std_message_table;
|
||||
err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1;
|
||||
|
||||
err->addon_message_table = NULL;
|
||||
err->first_addon_message = 0; /* for safety */
|
||||
err->last_addon_message = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
273
neo/renderer/jpeg-6/jerror.h
Normal file
273
neo/renderer/jpeg-6/jerror.h
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* jerror.h
|
||||
*
|
||||
* Copyright (C) 1994-1995, Thomas G. Lane.
|
||||
* This file is part of the Independent JPEG Group's software.
|
||||
* For conditions of distribution and use, see the accompanying README file.
|
||||
*
|
||||
* This file defines the error and message codes for the JPEG library.
|
||||
* Edit this file to add new codes, or to translate the message strings to
|
||||
* some other language.
|
||||
* A set of error-reporting macros are defined too. Some applications using
|
||||
* the JPEG library may wish to include this file to get the error codes
|
||||
* and/or the macros.
|
||||
*/
|
||||
|
||||
/*
|
||||
* To define the enum list of message codes, include this file without
|
||||
* defining macro JMESSAGE. To create a message string table, include it
|
||||
* again with a suitable JMESSAGE definition (see jerror.c for an example).
|
||||
*/
|
||||
#ifndef JMESSAGE
|
||||
#ifndef JERROR_H
|
||||
/* First time through, define the enum list */
|
||||
#define JMAKE_ENUM_LIST
|
||||
#else
|
||||
/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */
|
||||
#define JMESSAGE(code,string)
|
||||
#endif /* JERROR_H */
|
||||
#endif /* JMESSAGE */
|
||||
|
||||
#ifdef JMAKE_ENUM_LIST
|
||||
|
||||
typedef enum {
|
||||
|
||||
#define JMESSAGE(code,string) code ,
|
||||
|
||||
#endif /* JMAKE_ENUM_LIST */
|
||||
|
||||
JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */
|
||||
|
||||
/* For maintenance convenience, list is alphabetical by message code name */
|
||||
JMESSAGE(JERR_ARITH_NOTIMPL,
|
||||
"Sorry, there are legal restrictions on arithmetic coding")
|
||||
JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix")
|
||||
JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix")
|
||||
JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode")
|
||||
JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS")
|
||||
JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported")
|
||||
JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace")
|
||||
JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace")
|
||||
JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length")
|
||||
JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan")
|
||||
JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d")
|
||||
JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d")
|
||||
JMESSAGE(JERR_BAD_PROGRESSION,
|
||||
"Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d")
|
||||
JMESSAGE(JERR_BAD_PROG_SCRIPT,
|
||||
"Invalid progressive parameters at scan script entry %d")
|
||||
JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors")
|
||||
JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d")
|
||||
JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d")
|
||||
JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access")
|
||||
JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small")
|
||||
JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here")
|
||||
JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet")
|
||||
JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d")
|
||||
JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request")
|
||||
JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d")
|
||||
JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x")
|
||||
JMESSAGE(JERR_DHT_COUNTS, "Bogus DHT counts")
|
||||
JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d")
|
||||
JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d")
|
||||
JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)")
|
||||
JMESSAGE(JERR_EMS_READ, "Read from EMS failed")
|
||||
JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed")
|
||||
JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan")
|
||||
JMESSAGE(JERR_FILE_READ, "Input file read error")
|
||||
JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?")
|
||||
JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet")
|
||||
JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow")
|
||||
JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry")
|
||||
JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels")
|
||||
JMESSAGE(JERR_INPUT_EMPTY, "Empty input file")
|
||||
JMESSAGE(JERR_INPUT_EOF, "Premature end of input file")
|
||||
JMESSAGE(JERR_MISMATCHED_QUANT_TABLE,
|
||||
"Cannot transcode due to multiple use of quantization table %d")
|
||||
JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data")
|
||||
JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change")
|
||||
JMESSAGE(JERR_NOTIMPL, "Not implemented yet")
|
||||
JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time")
|
||||
JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported")
|
||||
JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined")
|
||||
JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image")
|
||||
JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined")
|
||||
JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x")
|
||||
JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)")
|
||||
JMESSAGE(JERR_QUANT_COMPONENTS,
|
||||
"Cannot quantize more than %d color components")
|
||||
JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors")
|
||||
JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors")
|
||||
JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers")
|
||||
JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker")
|
||||
JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x")
|
||||
JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers")
|
||||
JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF")
|
||||
JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s")
|
||||
JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file")
|
||||
JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file")
|
||||
JMESSAGE(JERR_TFILE_WRITE,
|
||||
"Write failed on temporary file --- out of disk space?")
|
||||
JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines")
|
||||
JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x")
|
||||
JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up")
|
||||
JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation")
|
||||
JMESSAGE(JERR_XMS_READ, "Read from XMS failed")
|
||||
JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed")
|
||||
JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT)
|
||||
JMESSAGE(JMSG_VERSION, JVERSION)
|
||||
JMESSAGE(JTRC_16BIT_TABLES,
|
||||
"Caution: quantization tables are too coarse for baseline JPEG")
|
||||
JMESSAGE(JTRC_ADOBE,
|
||||
"Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d")
|
||||
JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u")
|
||||
JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u")
|
||||
JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x")
|
||||
JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x")
|
||||
JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d")
|
||||
JMESSAGE(JTRC_DRI, "Define Restart Interval %u")
|
||||
JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u")
|
||||
JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u")
|
||||
JMESSAGE(JTRC_EOI, "End Of Image")
|
||||
JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d")
|
||||
JMESSAGE(JTRC_JFIF, "JFIF APP0 marker, density %dx%d %d")
|
||||
JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE,
|
||||
"Warning: thumbnail image size does not match data length %u")
|
||||
JMESSAGE(JTRC_JFIF_MINOR, "Unknown JFIF minor revision number %d.%02d")
|
||||
JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image")
|
||||
JMESSAGE(JTRC_MISC_MARKER, "Skipping marker 0x%02x, length %u")
|
||||
JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x")
|
||||
JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u")
|
||||
JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors")
|
||||
JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors")
|
||||
JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization")
|
||||
JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d")
|
||||
JMESSAGE(JTRC_RST, "RST%d")
|
||||
JMESSAGE(JTRC_SMOOTH_NOTIMPL,
|
||||
"Smoothing not supported with nonstandard sampling ratios")
|
||||
JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d")
|
||||
JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d")
|
||||
JMESSAGE(JTRC_SOI, "Start of Image")
|
||||
JMESSAGE(JTRC_SOS, "Start Of Scan: %d components")
|
||||
JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d")
|
||||
JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d")
|
||||
JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s")
|
||||
JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s")
|
||||
JMESSAGE(JTRC_UNKNOWN_IDS,
|
||||
"Unrecognized component IDs %d %d %d, assuming YCbCr")
|
||||
JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u")
|
||||
JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u")
|
||||
JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d")
|
||||
JMESSAGE(JWRN_BOGUS_PROGRESSION,
|
||||
"Inconsistent progression sequence for component %d coefficient %d")
|
||||
JMESSAGE(JWRN_EXTRANEOUS_DATA,
|
||||
"Corrupt JPEG data: %u extraneous bytes before marker 0x%02x")
|
||||
JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment")
|
||||
JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code")
|
||||
JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d")
|
||||
JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file")
|
||||
JMESSAGE(JWRN_MUST_RESYNC,
|
||||
"Corrupt JPEG data: found marker 0x%02x instead of RST%d")
|
||||
JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG")
|
||||
JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines")
|
||||
|
||||
#ifdef JMAKE_ENUM_LIST
|
||||
|
||||
JMSG_LASTMSGCODE
|
||||
} J_MESSAGE_CODE;
|
||||
|
||||
#undef JMAKE_ENUM_LIST
|
||||
#endif /* JMAKE_ENUM_LIST */
|
||||
|
||||
/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */
|
||||
#undef JMESSAGE
|
||||
|
||||
|
||||
#ifndef JERROR_H
|
||||
#define JERROR_H
|
||||
|
||||
/* Macros to simplify using the error and trace message stuff */
|
||||
/* The first parameter is either type of cinfo pointer */
|
||||
|
||||
/* Fatal errors (print message and exit) */
|
||||
#define ERREXIT(cinfo,code) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
(*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
|
||||
#define ERREXIT1(cinfo,code,p1) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
(cinfo)->err->msg_parm.i[0] = (p1), \
|
||||
(*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
|
||||
#define ERREXIT2(cinfo,code,p1,p2) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
(cinfo)->err->msg_parm.i[0] = (p1), \
|
||||
(cinfo)->err->msg_parm.i[1] = (p2), \
|
||||
(*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
|
||||
#define ERREXIT3(cinfo,code,p1,p2,p3) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
(cinfo)->err->msg_parm.i[0] = (p1), \
|
||||
(cinfo)->err->msg_parm.i[1] = (p2), \
|
||||
(cinfo)->err->msg_parm.i[2] = (p3), \
|
||||
(*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
|
||||
#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
(cinfo)->err->msg_parm.i[0] = (p1), \
|
||||
(cinfo)->err->msg_parm.i[1] = (p2), \
|
||||
(cinfo)->err->msg_parm.i[2] = (p3), \
|
||||
(cinfo)->err->msg_parm.i[3] = (p4), \
|
||||
(*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
|
||||
#define ERREXITS(cinfo,code,str) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \
|
||||
(*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))
|
||||
|
||||
#define MAKESTMT(stuff) do { stuff } while (0)
|
||||
|
||||
/* Nonfatal errors (we can keep going, but the data is probably corrupt) */
|
||||
#define WARNMS(cinfo,code) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
(*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1))
|
||||
#define WARNMS1(cinfo,code,p1) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
(cinfo)->err->msg_parm.i[0] = (p1), \
|
||||
(*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1))
|
||||
#define WARNMS2(cinfo,code,p1,p2) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
(cinfo)->err->msg_parm.i[0] = (p1), \
|
||||
(cinfo)->err->msg_parm.i[1] = (p2), \
|
||||
(*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1))
|
||||
|
||||
/* Informational/debugging messages */
|
||||
#define TRACEMS(cinfo,lvl,code) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
(*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)))
|
||||
#define TRACEMS1(cinfo,lvl,code,p1) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
(cinfo)->err->msg_parm.i[0] = (p1), \
|
||||
(*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)))
|
||||
#define TRACEMS2(cinfo,lvl,code,p1,p2) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
(cinfo)->err->msg_parm.i[0] = (p1), \
|
||||
(cinfo)->err->msg_parm.i[1] = (p2), \
|
||||
(*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)))
|
||||
#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \
|
||||
MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \
|
||||
_mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \
|
||||
(cinfo)->err->msg_code = (code); \
|
||||
(*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); )
|
||||
#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \
|
||||
MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \
|
||||
_mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \
|
||||
(cinfo)->err->msg_code = (code); \
|
||||
(*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); )
|
||||
#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \
|
||||
MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \
|
||||
_mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \
|
||||
_mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \
|
||||
(cinfo)->err->msg_code = (code); \
|
||||
(*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); )
|
||||
#define TRACEMSS(cinfo,lvl,code,str) \
|
||||
((cinfo)->err->msg_code = (code), \
|
||||
strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \
|
||||
(*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)))
|
||||
|
||||
#endif /* JERROR_H */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user