2
// Copyright (C) 1997 - 2001, Paul C. Gregory
4
// Contact: pgregory@aqsis.com
6
// This library is free software; you can redistribute it and/or
7
// modify it under the terms of the GNU General Public
8
// License as published by the Free Software Foundation; either
9
// version 2 of the License, or (at your option) any later version.
11
// This library is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
// General Public License for more details.
16
// You should have received a copy of the GNU General Public
17
// License along with this library; if not, write to the Free Software
18
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
\brief Implements the CqBucket class responsible for bookkeeping the primitives and storing the results.
23
\author Paul C. Gregory (pgregory@aqsis.com)
34
#include "imagepixel.h"
43
START_NAMESPACE( Aqsis )
46
//----------------------------------------------------------------------
47
/** Static data on CqBucket
50
TqInt CqBucket::m_XSize;
51
TqInt CqBucket::m_YSize;
52
TqInt CqBucket::m_FilterXWidth;
53
TqInt CqBucket::m_FilterYWidth;
54
TqInt CqBucket::m_XMax;
55
TqInt CqBucket::m_YMax;
56
TqInt CqBucket::m_XOrigin;
57
TqInt CqBucket::m_YOrigin;
58
TqInt CqBucket::m_PixelXSamples;
59
TqInt CqBucket::m_PixelYSamples;
60
std::vector<CqImagePixel> CqBucket::m_aieImage;
61
std::vector<std::vector<CqVector2D> > CqBucket::m_aSamplePositions;
62
std::vector<TqFloat> CqBucket::m_aFilterValues;
63
std::vector<TqFloat> CqBucket::m_aDatas;
64
std::vector<TqFloat> CqBucket::m_aCoverages;
67
//----------------------------------------------------------------------
68
/** Initialise the static image storage area.
69
* Clear,Allocate, Init. the m_aieImage samples
72
void CqBucket::InitialiseBucket( TqInt xorigin, TqInt yorigin, TqInt xsize, TqInt ysize, TqInt xfwidth, TqInt yfwidth, TqInt xsamples, TqInt ysamples, TqBool fJitter )
78
m_FilterXWidth = xfwidth;
79
m_FilterYWidth = yfwidth;
80
m_XMax = static_cast<TqInt>( CEIL( ( xfwidth - 1 ) * 0.5f ) );
81
m_YMax = static_cast<TqInt>( CEIL( ( xfwidth - 1 ) * 0.5f ) );
82
m_PixelXSamples = xsamples;
83
m_PixelYSamples = ysamples;
85
TqInt ywidth1, xwidth1;
86
ywidth1 = m_YSize + m_FilterYWidth;
87
xwidth1 = m_XSize + m_FilterXWidth;
89
// Allocate the image element storage if this is the first bucket
90
if(m_aieImage.empty())
92
m_aieImage.resize( xwidth1 * ywidth1);
93
m_aSamplePositions.resize( xwidth1 * ywidth1 );
95
// Initialise the samples for this bucket.
97
for ( TqInt i = 0; i < ywidth1; i++ )
99
for ( TqInt j = 0; j < xwidth1; j++ )
101
m_aieImage[which].Clear();
102
m_aieImage[which].AllocateSamples( xsamples, ysamples );
103
m_aieImage[which].InitialiseSamples( m_aSamplePositions[which], fJitter );
110
// now shuffle the pixels around and add in the pixel offset to the position.
111
static CqRandom random( 53 );
112
TqInt shuffleX = random.RandomInt( xwidth1 );
113
TqInt shuffleY = random.RandomInt( ywidth1 );
115
TqInt sourceIndex = shuffleY*xwidth1 + shuffleX;
116
TqInt numPixels = xwidth1*ywidth1;
117
for ( TqInt i = 0; i < ywidth1; i++ )
119
for ( TqInt j = 0; j < xwidth1; j++ )
121
CqVector2D bPos2( m_XOrigin, m_YOrigin );
122
bPos2 += CqVector2D( ( j - m_FilterXWidth / 2 ), ( i - m_FilterYWidth / 2 ) );
124
m_aieImage[which].Clear();
125
m_aieImage[which].OffsetSamples( bPos2, m_aSamplePositions[sourceIndex] );
128
sourceIndex = (sourceIndex+1) % (numPixels);
134
//----------------------------------------------------------------------
135
/** Initialise the static filter values.
138
void CqBucket::InitialiseFilterValues()
140
if( !m_aFilterValues.empty() )
143
// Allocate and fill in the filter values array for each pixel.
144
TqInt numsubpixels = ( m_PixelXSamples * m_PixelYSamples );
145
TqInt numperpixel = numsubpixels * numsubpixels;
147
TqUint numvalues = static_cast<TqUint>( ( ( m_FilterXWidth + 1 ) * ( m_FilterYWidth + 1 ) ) * ( numperpixel ) );
149
m_aFilterValues.resize( numvalues );
151
RtFilterFunc pFilter;
152
pFilter = QGetRenderContext() ->optCurrent().funcFilter();
155
if( NULL == pFilter )
156
pFilter = RiBoxFilter;
158
TqFloat xmax = m_XMax;
159
TqFloat ymax = m_YMax;
160
TqFloat xfwo2 = m_FilterXWidth * 0.5f;
161
TqFloat yfwo2 = m_FilterYWidth * 0.5f;
162
TqFloat xfw = m_FilterXWidth;
164
TqFloat subcellwidth = 1.0f / numsubpixels;
165
TqFloat subcellcentre = subcellwidth * 0.5f;
167
// Go over every pixel touched by the filter
169
for ( py = static_cast<TqInt>( -ymax ); py <= static_cast<TqInt>( ymax ); py++ )
171
for( px = static_cast<TqInt>( -xmax ); px <= static_cast<TqInt>( xmax ); px++ )
173
// Get the index of the pixel in the array.
174
TqInt index = static_cast<TqInt>( ( ( ( py + ymax ) * xfw ) + ( px + xmax ) ) * numperpixel );
175
TqFloat pfx = px - 0.5f;
176
TqFloat pfy = py - 0.5f;
177
// Go over every subpixel in the pixel.
179
for ( sy = 0; sy < m_PixelYSamples; sy++ )
181
for ( sx = 0; sx < m_PixelXSamples; sx++ )
183
// Get the index of the subpixel in the array
184
TqInt sindex = index + ( ( ( sy * m_PixelXSamples ) + sx ) * numsubpixels );
185
TqFloat sfx = static_cast<TqFloat>( sx ) / m_PixelXSamples;
186
TqFloat sfy = static_cast<TqFloat>( sy ) / m_PixelYSamples;
187
// Go over each subcell in the subpixel
189
for ( cy = 0; cy < m_PixelXSamples; cy++ )
191
for ( cx = 0; cx < m_PixelYSamples; cx++ )
193
// Get the index of the subpixel in the array
194
TqInt cindex = sindex + ( ( cy * m_PixelYSamples ) + cx );
195
TqFloat fx = ( cx * subcellwidth ) + sfx + pfx + subcellcentre;
196
TqFloat fy = ( cy * subcellwidth ) + sfy + pfy + subcellcentre;
198
if ( fx >= -xfwo2 && fy >= -yfwo2 && fx <= xfwo2 && fy <= yfwo2 )
199
w = ( *pFilter ) ( fx, fy, m_FilterXWidth, m_FilterYWidth );
200
m_aFilterValues[ cindex ] = w;
211
//----------------------------------------------------------------------
212
/** Combine the subsamples into single pixel samples and coverage information.
215
void CqBucket::CombineElements()
217
std::vector<CqImagePixel>::iterator end = m_aieImage.end();
218
for ( std::vector<CqImagePixel>::iterator i = m_aieImage.begin(); i != end ; i++ )
223
//----------------------------------------------------------------------
224
/** Get the sample color for the specified screen position.
225
* If position is outside bucket, returns black.
226
* \param iXPos Screen position of sample.
227
* \param iYPos Screen position of sample.
230
CqColor CqBucket::Color( TqInt iXPos, TqInt iYPos )
233
ImageElement( iXPos, iYPos, pie );
235
return ( pie->Color() );
240
//----------------------------------------------------------------------
241
/** Get the sample opacity for the specified screen position.
242
* If position is outside bucket, returns black.
243
* \param iXPos Screen position of sample.
244
* \param iYPos Screen position of sample.
247
CqColor CqBucket::Opacity( TqInt iXPos, TqInt iYPos )
250
ImageElement( iXPos, iYPos, pie );
252
return ( pie->Opacity() );
258
//----------------------------------------------------------------------
259
/** Get the sample coverage for the specified screen position.
260
* If position is outside bucket, returns 0.
261
* \param iXPos Screen position of sample.
262
* \param iYPos Screen position of sample.
265
TqFloat CqBucket::Coverage( TqInt iXPos, TqInt iYPos )
268
ImageElement( iXPos, iYPos, pie );
270
return ( pie->Coverage() );
276
//----------------------------------------------------------------------
277
/** Get the sample depth for the specified screen position.
278
* If position is outside bucket, returns FLT_MAX.
279
* \param iXPos Screen position of sample.
280
* \param iYPos Screen position of sample.
283
TqFloat CqBucket::Depth( TqInt iXPos, TqInt iYPos )
286
ImageElement( iXPos, iYPos, pie );
288
return ( pie->Depth() );
294
//----------------------------------------------------------------------
295
/** Get the maximum sample depth for the specified screen position.
296
* If position is outside bucket, returns FLT_MAX.
297
* \param iXPos Screen position of sample.
298
* \param iYPos Screen position of sample.
301
TqFloat CqBucket::MaxDepth( TqInt iXPos, TqInt iYPos )
304
ImageElement( iXPos, iYPos, pie );
306
return ( pie->MaxDepth() );
312
//----------------------------------------------------------------------
313
/** Get a pointer to the samples for a given pixel.
314
* If position is outside bucket, returns NULL.
315
* \param iXPos Screen position of sample.
316
* \param iYPos Screen position of sample.
319
const TqFloat* CqBucket::Data( TqInt iXPos, TqInt iYPos )
322
ImageElement( iXPos, iYPos, pie );
324
return ( pie->Data() );
329
//----------------------------------------------------------------------
330
/** Get count of samples.
331
* If position is outside bucket, returns 0.
332
* \param iXPos Screen position of sample.
333
* \param iYPos Screen position of sample.
336
TqInt CqBucket::DataSize( TqInt iXPos, TqInt iYPos )
339
ImageElement( iXPos, iYPos, pie );
341
return ( pie->DataSize() );
347
//----------------------------------------------------------------------
348
/** Filter the samples in this bucket according to type and filter widths.
351
void CqBucket::FilterBucket(TqBool empty)
355
TqInt datasize = QGetRenderContext()->GetOutputDataTotalSize();
356
m_aDatas.resize( datasize * Width() * Height() );
357
m_aCoverages.resize( Width() * Height() );
359
TqInt xmax = static_cast<TqInt>( CEIL( ( FilterXWidth() - 1 ) * 0.5f ) );
360
TqInt ymax = static_cast<TqInt>( CEIL( ( FilterYWidth() - 1 ) * 0.5f ) );
361
TqFloat xfwo2 = FilterXWidth() * 0.5f;
362
TqFloat yfwo2 = FilterYWidth() * 0.5f;
363
TqInt numsubpixels = ( m_PixelXSamples * m_PixelYSamples );
365
TqInt numperpixel = numsubpixels * numsubpixels;
366
TqInt xlen = Width() + FilterXWidth();
368
TqInt SampleCount = 0;
374
TqBool fImager = ( QGetRenderContext() ->optCurrent().GetStringOption( "System", "Imager" ) [ 0 ] != "null" );
376
TqInt endy = YOrigin() + Height();
377
TqInt endx = XOrigin() + Width();
379
for ( y = YOrigin(); y < endy ; y++ )
381
TqFloat ycent = y + 0.5f;
382
for ( x = XOrigin(); x < endx ; x++ )
384
TqFloat xcent = x + 0.5f;
387
std::valarray<TqFloat> samples( 0.0f, datasize);
392
// Get the element at the upper left corner of the filter area.
393
ImageElement( x - xmax, y - ymax, pie );
394
for ( fy = -ymax; fy <= ymax; fy++ )
396
CqImagePixel* pie2 = pie;
397
for ( fx = -xmax; fx <= xmax; fx++ )
399
TqInt index = ( ( ( fy + ymax ) * FilterXWidth() ) + ( fx + xmax ) ) * numperpixel;
400
// Now go over each subsample within the pixel
402
TqInt sampleIndex = 0;
403
for ( sy = 0; sy < m_PixelYSamples; sy++ )
405
for ( sx = 0; sx < m_PixelXSamples; sx++ )
407
TqInt sindex = index + ( ( ( sy * m_PixelXSamples ) + sx ) * numsubpixels );
408
const SqSampleData& sampleData = pie2->SampleData( sampleIndex );
409
CqVector2D vecS = sampleData.m_Position;
410
vecS -= CqVector2D( xcent, ycent );
411
if ( vecS.x() >= -xfwo2 && vecS.y() >= -yfwo2 && vecS.x() <= xfwo2 && vecS.y() <= yfwo2 )
413
TqInt cindex = sindex + sampleData.m_SubCellIndex;
414
TqFloat g = m_aFilterValues[ cindex ];
416
if ( pie2->Values( sx, sy ).size() > 0 )
418
SqImageSample* pSample = &pie2->Values( sx, sy ) [ 0 ];
419
samples += pSample->m_Data * g;
433
for ( TqInt k = 0; k < datasize; k ++)
434
m_aDatas[ i*datasize + k ] = samples[k] / gTot;
436
// Set depth to infinity if no samples.
437
if ( SampleCount <= 0 )
438
m_aDatas[ i*datasize+6 ] = FLT_MAX;
440
if ( SampleCount >= numsubpixels)
441
m_aCoverages[ i ] = 1.0;
443
m_aCoverages[ i ] = ( TqFloat ) SampleCount / ( TqFloat ) (numsubpixels );
450
ImageElement( XOrigin(), YOrigin(), pie );
454
for ( y = 0; y < endy; y++ )
456
CqImagePixel* pie2 = pie;
457
for ( x = 0; x < endx; x++ )
459
for (TqInt k=0; k < datasize; k++)
460
pie2->GetPixelSample().m_Data[k] = m_aDatas[ i * datasize + k ];
461
pie2->SetCoverage( m_aCoverages[ i++ ] );
467
endy = YOrigin() + Height();
468
endx = XOrigin() + Width();
470
if ( NULL != QGetRenderContext() ->optCurrent().pshadImager() && NULL != QGetRenderContext() ->optCurrent().pshadImager() ->pShader() )
472
QGetRenderContext() ->Stats().MakeFilterBucket().Stop();
473
// Init & Execute the imager shader
475
QGetRenderContext() ->optCurrent().InitialiseColorImager( this );
480
ImageElement( XOrigin(), YOrigin(), pie );
481
for ( y = YOrigin(); y < endy ; y++ )
483
CqImagePixel* pie2 = pie;
484
for ( x = XOrigin(); x < endx ; x++ )
486
imager = QGetRenderContext() ->optCurrent().GetColorImager( x , y );
487
// Normal case will be to poke the alpha from the image shader and
488
// multiply imager color with it... but after investigation alpha is always
489
// == 1 after a call to imager shader in 3delight and BMRT.
490
// Therefore I did not ask for alpha value and set directly the pCols[i]
491
// with imager value. see imagers.cpp
492
pie2->SetColor( imager );
493
imager = QGetRenderContext() ->optCurrent().GetOpacityImager( x , y );
494
pie2->SetOpacity( imager );
501
QGetRenderContext() ->Stats().MakeFilterBucket().Start();
506
//----------------------------------------------------------------------
507
/** Expose the samples in this bucket according to specified gain and gamma settings.
510
void CqBucket::ExposeBucket()
512
if ( QGetRenderContext() ->optCurrent().GetFloatOption( "System", "Exposure" ) [ 0 ] == 1.0 &&
513
QGetRenderContext() ->optCurrent().GetFloatOption( "System", "Exposure" ) [ 1 ] == 1.0 )
518
ImageElement( XOrigin(), YOrigin(), pie );
520
TqFloat exposegain = QGetRenderContext() ->optCurrent().GetFloatOption( "System", "Exposure" ) [ 0 ];
521
TqFloat exposegamma = QGetRenderContext() ->optCurrent().GetFloatOption( "System", "Exposure" ) [ 1 ];
522
TqFloat oneovergamma = 1.0f / exposegamma;
527
nextx = Width() + FilterXWidth();
529
for ( y = 0; y < endy; y++ )
531
CqImagePixel* pie2 = pie;
532
for ( x = 0; x < endx; x++ )
534
// color=(color*gain)^1/gamma
535
if ( exposegain != 1.0 )
536
pie2->SetColor( pie2->Color() * exposegain );
538
if ( exposegamma != 1.0 )
540
CqColor col = pie2->Color();
541
col.SetfRed ( pow( col.fRed (), oneovergamma ) );
542
col.SetfGreen( pow( col.fGreen(), oneovergamma ) );
543
col.SetfBlue ( pow( col.fBlue (), oneovergamma ) );
544
pie2->SetColor( col );
554
//----------------------------------------------------------------------
555
/** Quantize the samples in this bucket according to type.
558
void CqBucket::QuantizeBucket()
560
// Initiliaze the random with a value based on the X,Y coordinate
561
static CqRandom random( 61 );
566
nextx = Width() + FilterXWidth();
569
if ( QGetRenderContext() ->optCurrent().GetIntegerOption( "System", "DisplayMode" ) [ 0 ] & ModeRGB )
571
const TqFloat* pQuant = QGetRenderContext() ->optCurrent().GetFloatOption( "Quantize", "Color" );
572
TqInt one = static_cast<TqInt>( pQuant [ 0 ] );
573
TqInt min = static_cast<TqInt>( pQuant [ 1 ] );
574
TqInt max = static_cast<TqInt>( pQuant [ 2 ] );
575
double ditheramplitude = pQuant [ 3 ];
577
// If settings are 0,0,0,0 then leave as floating point and we will save an FP tiff.
578
if ( one == 0 && min == 0 && max == 0 )
582
ImageElement( XOrigin(), YOrigin(), pie );
585
for ( y = 0; y < endy; y++ )
587
CqImagePixel* pie2 = pie;
588
for ( x = 0; x < endx; x++ )
591
double _or, _og, _ob;
592
double s = random.RandomFloat();
593
CqColor col = pie2->Color();
594
CqColor opa = pie2->Opacity();
595
if ( modf( one * col.fRed () + ditheramplitude * s, &r ) > 0.5 ) r += 1;
596
if ( modf( one * col.fGreen() + ditheramplitude * s, &g ) > 0.5 ) g += 1;
597
if ( modf( one * col.fBlue () + ditheramplitude * s, &b ) > 0.5 ) b += 1;
598
if ( modf( one * opa.fRed () + ditheramplitude * s, &_or ) > 0.5 ) _or += 1;
599
if ( modf( one * opa.fGreen() + ditheramplitude * s, &_og ) > 0.5 ) _og += 1;
600
if ( modf( one * opa.fBlue () + ditheramplitude * s, &_ob ) > 0.5 ) _ob += 1;
601
r = CLAMP( r, min, max );
602
g = CLAMP( g, min, max );
603
b = CLAMP( b, min, max );
604
_or = CLAMP( _or, min, max );
605
_og = CLAMP( _og, min, max );
606
_ob = CLAMP( _ob, min, max );
611
opa.SetfGreen( _og );
612
opa.SetfBlue ( _ob );
613
pie2->SetColor( col );
614
pie2->SetOpacity( opa );
622
if ( QGetRenderContext() ->optCurrent().GetIntegerOption( "System", "DisplayMode" ) [ 0 ] & ModeZ )
624
const TqFloat* pQuant = QGetRenderContext() ->optCurrent().GetFloatOption( "Quantize", "Depth" );
625
TqInt one = static_cast<TqInt>( pQuant [ 0 ] );
626
TqInt min = static_cast<TqInt>( pQuant [ 1 ] );
627
TqInt max = static_cast<TqInt>( pQuant [ 2 ] );
628
double ditheramplitude = pQuant [ 3 ];
629
if( ditheramplitude == 0.0f && one == 0 && min == 0 && max == 0 )
633
ImageElement( XOrigin(), YOrigin(), pie );
635
for ( y = 0; y < endy; y++ )
637
CqImagePixel* pie2 = pie;
638
for ( x = 0; x < endx; x++ )
641
if ( modf( one * pie2->Depth() + ditheramplitude * random.RandomFloat(), &d ) > 0.5 ) d += 1;
642
d = CLAMP( d, min, max );
650
// Now go through the other AOV's and quantize those if necessary.
651
std::map<std::string, CqRenderer::SqOutputDataEntry>& DataMap = QGetRenderContext()->GetMapOfOutputDataEntries();
652
std::map<std::string, CqRenderer::SqOutputDataEntry>::iterator entry;
653
for( entry = DataMap.begin(); entry != DataMap.end(); entry++ )
655
const TqFloat* pQuant = QGetRenderContext() ->optCurrent().GetFloatOption( "Quantize", entry->first.c_str() );
658
TqInt startindex = entry->second.m_Offset;
659
TqInt endindex = startindex + entry->second.m_NumSamples;
660
TqInt one = static_cast<TqInt>( pQuant [ 0 ] );
661
TqInt min = static_cast<TqInt>( pQuant [ 1 ] );
662
TqInt max = static_cast<TqInt>( pQuant [ 2 ] );
663
double ditheramplitude = pQuant [ 3 ];
666
ImageElement( XOrigin(), YOrigin(), pie );
668
for ( y = 0; y < endy; y++ )
670
CqImagePixel* pie2 = pie;
671
for ( x = 0; x < endx; x++ )
674
for( sampleindex = startindex; sampleindex < endindex; sampleindex++ )
677
if ( modf( one * pie2->GetPixelSample().m_Data[sampleindex] + ditheramplitude * random.RandomFloat(), &d ) > 0.5 ) d += 1.0f;
678
d = CLAMP( d, min, max );
679
pie2->GetPixelSample().m_Data[sampleindex] = d;
689
//----------------------------------------------------------------------
690
/** Clear any data on the bucket
692
void CqBucket::ShutdownBucket()
695
m_aFilterValues.clear();
696
m_aCoverages.clear();
698
std::vector<std::vector<CqVector2D> >::iterator i;
699
for( i=m_aSamplePositions.begin(); i!=m_aSamplePositions.end(); i++ )
701
m_aSamplePositions.clear();
704
//---------------------------------------------------------------------
706
END_NAMESPACE( Aqsis )