diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 80b4aeb50..7de329e48 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -140,11 +140,13 @@ jobs: - name: ArduinoBLE - name: ArduinoGraphics - name: Arduino_GigaDisplayTouch + - name: arducam_dvp additional-sketch-paths: | - libraries/PDM - libraries/MCUboot - libraries/Camera/examples/CameraCaptureRawBytes - libraries/Camera/examples/CameraMotionDetect + - libraries/Camera/examples/GigaCameraDisplay - libraries/Portenta_SDCARD - libraries/Portenta_SDRAM - libraries/Arduino_H7_Video diff --git a/libraries/Camera/examples/GigaCameraDisplay/GigaCameraDisplay.ino b/libraries/Camera/examples/GigaCameraDisplay/GigaCameraDisplay.ino index d9218000d..4c1328d21 100644 --- a/libraries/Camera/examples/GigaCameraDisplay/GigaCameraDisplay.ino +++ b/libraries/Camera/examples/GigaCameraDisplay/GigaCameraDisplay.ino @@ -1,28 +1,40 @@ -#include "camera.h" -#include "Portenta_lvgl.h" -#include "Portenta_Video.h" +#include "arducam_dvp.h" +#include "Arduino_H7_Video.h" +#include "dsi.h" +#include "SDRAM.h" -#if 0 -#include "gc2145.h" -GC2145 galaxyCore; -Camera cam(galaxyCore); -#define IMAGE_MODE CAMERA_RGB565 -#else -#include "himax.h" +// This example only works with Greyscale cameras (due to the palette + resize&rotate algo) +#define ARDUCAM_CAMERA_HM01B0 + +#ifdef ARDUCAM_CAMERA_HM01B0 +#include "Himax_HM01B0/himax.h" HM01B0 himax; Camera cam(himax); #define IMAGE_MODE CAMERA_GRAYSCALE +#elif defined(ARDUCAM_CAMERA_HM0360) +#include "Himax_HM0360/hm0360.h" +HM0360 himax; +Camera cam(himax); +#define IMAGE_MODE CAMERA_GRAYSCALE +#elif defined(ARDUCAM_CAMERA_OV767X) +#include "OV7670/ov767x.h" +// OV7670 ov767x; +OV7675 ov767x; +Camera cam(ov767x); +#define IMAGE_MODE CAMERA_RGB565 +#elif defined(ARDUCAM_CAMERA_GC2145) +#include "GC2145/gc2145.h" +GC2145 galaxyCore; +Camera cam(galaxyCore); +#define IMAGE_MODE CAMERA_RGB565 #endif -/* - Other buffer instantiation options: - FrameBuffer fb(0x30000000); - FrameBuffer fb(320,240,2); -*/ +// The buffer used to capture the frame FrameBuffer fb; - -unsigned long lastUpdate = 0; - +// The buffer used to rotate and resize the frame +FrameBuffer outfb; +// The buffer used to rotate and resize the frame +Arduino_H7_Video Display(800, 480, GigaDisplayShield); void blinkLED(uint32_t count = 0xFFFFFFFF) { @@ -34,68 +46,61 @@ void blinkLED(uint32_t count = 0xFFFFFFFF) delay(50); // wait for a second } } -void LCD_ST7701_Init(); uint32_t palette[256]; void setup() { - pinMode(PA_1, OUTPUT); - digitalWrite(PA_1, HIGH); - - pinMode(PD_4, OUTPUT); - digitalWrite(PD_4, LOW); - // Init the cam QVGA, 30FPS if (!cam.begin(CAMERA_R320x240, IMAGE_MODE, 30)) { blinkLED(); } + // Setup the palette to convert 8 bit greyscale to 32bit greyscale for (int i = 0; i < 256; i++) { palette[i] = 0xFF000000 | (i << 16) | (i << 8) | i; } - giga_init_video(); - LCD_ST7701_Init(); + Display.begin(); - blinkLED(5); + if (IMAGE_MODE == CAMERA_GRAYSCALE) { + dsi_configueCLUT((uint32_t*)palette); + } + outfb.setBuffer((uint8_t*)SDRAM.malloc(1024 * 1024)); - stm32_configue_CLUT((uint32_t*)palette); - stm32_LCD_Clear(0); - stm32_LCD_Clear(0); - stm32_LCD_Clear(0); - stm32_LCD_Clear(0); + // clear the display (gives a nice black background) + dsi_lcdClear(0); + dsi_drawCurrentFrameBuffer(); + dsi_lcdClear(0); + dsi_drawCurrentFrameBuffer(); } -#include "avir.h" +#define HTONS(x) (((x >> 8) & 0x00FF) | ((x << 8) & 0xFF00)) void loop() { - lastUpdate = millis(); - - // Grab frame and write to serial + // Grab frame and write to another framebuffer if (cam.grabFrame(fb, 3000) == 0) { - //avir :: CImageResizer<> ImageResizer( 8 ); - //ImageResizer.resizeImage( (uint8_t*)fb.getBuffer(), 320, 240, 0, (uint8_t*)outfb.getBuffer(), 480, 320, 1, 0 ); - static FrameBuffer outfb(0x30000000); - //static FrameBuffer outfb(getFramebufferEnd()); - for (int i = 0; i < 300; i++) { + + // double the resolution and transpose (rotate by 90 degrees) in the same step + // this only works if the camera feed is 320x240 and the area where we want to display is 640x480 + for (int i = 0; i < 320; i++) { for (int j = 0; j < 240; j++) { - //((uint8_t*)outfb.getBuffer())[j * 240 + (320 - i)] = ((uint8_t*)fb.getBuffer())[i * 320 + j]; -#if 1 - ((uint8_t*)outfb.getBuffer())[j * 2 + (i * 2) * 480] = ((uint8_t*)fb.getBuffer())[i + j * 320]; - ((uint8_t*)outfb.getBuffer())[j * 2 + (i * 2) * 480 + 1] = ((uint8_t*)fb.getBuffer())[i + j * 320]; - ((uint8_t*)outfb.getBuffer())[j * 2 + (i*2+1) * 480] = ((uint8_t*)fb.getBuffer())[i + j * 320]; - ((uint8_t*)outfb.getBuffer())[j * 2 + (i*2+1) * 480 + 1] = ((uint8_t*)fb.getBuffer())[i + j * 320]; -#endif -#if 0 - ((uint8_t*)outfb.getBuffer())[j + i * 240] = ((uint8_t*)fb.getBuffer())[i + j * 320]; -#endif + if (IMAGE_MODE == CAMERA_GRAYSCALE) { + ((uint8_t*)outfb.getBuffer())[j * 2 + (i * 2) * 480] = ((uint8_t*)fb.getBuffer())[i + j * 320]; + ((uint8_t*)outfb.getBuffer())[j * 2 + (i * 2) * 480 + 1] = ((uint8_t*)fb.getBuffer())[i + j * 320]; + ((uint8_t*)outfb.getBuffer())[j * 2 + (i * 2 + 1) * 480] = ((uint8_t*)fb.getBuffer())[i + j * 320]; + ((uint8_t*)outfb.getBuffer())[j * 2 + (i * 2 + 1) * 480 + 1] = ((uint8_t*)fb.getBuffer())[i + j * 320]; + } else { + ((uint16_t*)outfb.getBuffer())[j * 2 + (i * 2) * 480] = HTONS(((uint16_t*)fb.getBuffer())[i + j * 320]); + ((uint16_t*)outfb.getBuffer())[j * 2 + (i * 2) * 480 + 1] = HTONS(((uint16_t*)fb.getBuffer())[i + j * 320]); + ((uint16_t*)outfb.getBuffer())[j * 2 + (i * 2 + 1) * 480] = HTONS(((uint16_t*)fb.getBuffer())[i + j * 320]); + ((uint16_t*)outfb.getBuffer())[j * 2 + (i * 2 + 1) * 480 + 1] = HTONS(((uint16_t*)fb.getBuffer())[i + j * 320]); + } } } - //stm32_LCD_DrawImage((void*)outfb.getBuffer(), (void*)getNextFrameBuffer(), 240, 320, DMA2D_INPUT_L8); - stm32_LCD_DrawImage((void*)outfb.getBuffer(), (void*)getNextFrameBuffer(), 480, 640, DMA2D_INPUT_L8); - //stm32_LCD_DrawImage((void*)fb.getBuffer(), (void*)getNextFrameBuffer(), 320, 240, DMA2D_INPUT_L8); + dsi_lcdDrawImage((void*)outfb.getBuffer(), (void*)dsi_getCurrentFrameBuffer(), 480, 640, IMAGE_MODE == CAMERA_GRAYSCALE ? DMA2D_INPUT_L8 : DMA2D_INPUT_RGB565); + dsi_drawCurrentFrameBuffer(); } else { blinkLED(20); } -} +} \ No newline at end of file diff --git a/libraries/Camera/examples/GigaCameraDisplay/avir.h b/libraries/Camera/examples/GigaCameraDisplay/avir.h deleted file mode 100644 index ce4cec87a..000000000 --- a/libraries/Camera/examples/GigaCameraDisplay/avir.h +++ /dev/null @@ -1,6644 +0,0 @@ -//$ nobt -//$ nocpp - -/** - * @file avir.h - * - * @brief The "main" inclusion file with all required classes and functions. - * - * This is the "main" inclusion file for the "AVIR" image resizer. This - * inclusion file contains implementation of the AVIR image resizing algorithm - * in its entirety. Also includes several classes and functions that can be - * useful elsewhere. - * - * AVIR Copyright (c) 2015-2021 Aleksey Vaneev - * - * @mainpage - * - * @section intro_sec Introduction - * - * Description is available at https://github.com/avaneev/avir - * - * AVIR is devoted to women. Your digital photos can look good at any size! - * - * Please credit the author of this library in your documentation in the - * following way: "AVIR image resizing algorithm designed by Aleksey Vaneev". - * - * @section license License - * - * MIT License - * - * Copyright (c) 2015-2021 Aleksey Vaneev - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * @version 3.0 - */ - -#ifndef AVIR_CIMAGERESIZER_INCLUDED -#define AVIR_CIMAGERESIZER_INCLUDED - -#include -#include -#include -#include - -namespace avir { - -/** - * The macro defines AVIR version string. - */ - -#define AVIR_VERSION "3.0" - -/** - * The macro equals to "pi" constant, fills 53-bit floating point mantissa. - * Undefined at the end of file. - */ - -#define AVIR_PI 3.1415926535897932 - -/** - * The macro equals to "pi divided by 2" constant, fills 53-bit floating - * point mantissa. Undefined at the end of file. - */ - -#define AVIR_PId2 1.5707963267948966 - -/** - * A special macro that defines empty copy-constructor and copy operator with - * the "private:" prefix. This macro should be used in classes that cannot be - * copied in a standard C++ way. - */ - -#define AVIR_NOCTOR( ClassName ) \ - private: \ - ClassName( const ClassName& ) { } \ - ClassName& operator = ( const ClassName& ) { return( *this ); } - -/** - * Rounding function, based on the (int) typecast. Biased result. Not suitable - * for numbers >= 2^31. - * - * @param d Value to round. - * @return Rounded value. Some bias may be introduced. - */ - -template< class T > -inline T round( const T d ) -{ - return( d < (T) 0 ? -(T) (int) ( (T) 0.5 - d ) : - (T) (int) ( d + (T) 0.5 )); -} - -/** - * Template function "clamps" (clips) the specified value so that it is not - * lesser than "minv", and not greater than "maxv". - * - * @param Value Value to clamp. - * @param minv Minimal allowed value. - * @param maxv Maximal allowed value. - * @return The clamped value. - */ - -template< class T > -inline T clamp( const T& Value, const T minv, const T maxv ) -{ - if( Value < minv ) - { - return( minv ); - } - else - if( Value > maxv ) - { - return( maxv ); - } - else - { - return( Value ); - } -} - -/** - * Power 2.4 approximation function, designed for sRGB gamma correction. - * - * @param x Argument, in the range 0.09 to 1. - * @return Value raised into power 2.4, approximate. - */ - -template< class T > -inline T pow24_sRGB( const T x ) -{ - const double x2 = (double) x * x; - const double x3 = x2 * x; - const double x4 = x2 * x2; - - return( (T) ( 0.0985766365536824 + 0.839474952656502 * x2 + - 0.363287814061725 * x3 - 0.0125559718896615 / - ( 0.12758338921578 + 0.290283465468235 * x ) - - 0.231757513261358 * x - 0.0395365717969074 * x4 )); -} - -/** - * Power 1/2.4 approximation function, designed for sRGB gamma correction. - * - * @param x Argument, in the range 0.003 to 1. - * @return Value raised into power 1/2.4, approximate. - */ - -template< class T > -inline T pow24i_sRGB( const T x ) -{ - const double sx = sqrt( (double) x ); - const double ssx = sqrt( sx ); - const double sssx = sqrt( ssx ); - - return( (T) ( 0.000213364515060263 + 0.0149409239419218 * x + - 0.433973412731747 * sx + ssx * ( 0.659628181609715 * sssx - - 0.0380957908841466 - 0.0706476137208521 * sx ))); -} - -/** - * Function approximately linearizes the sRGB gamma value. - * - * @param s sRGB gamma value, in the range 0 to 1. - * @return Linearized sRGB gamma value, approximated. - */ - -template< class T > -inline T convertSRGB2Lin( const T s ) -{ - const T a = (T) 0.055; - - if( s <= (T) 0.04045 ) - { - return( s / (T) 12.92 ); - } - - return( pow24_sRGB(( s + a ) / ( (T) 1 + a ))); -} - -/** - * Function approximately de-linearizes the linear gamma value. - * - * @param s Linear gamma value, in the range 0 to 1. - * @return sRGB gamma value, approximated. - */ - -template< class T > -inline T convertLin2SRGB( const T s ) -{ - const T a = (T) 0.055; - - if( s <= (T) 0.0031308 ) - { - return( (T) 12.92 * s ); - } - - return(( (T) 1 + a ) * pow24i_sRGB( s ) - a ); -} - -/** - * Function converts (via typecast) specified array of type T1 values of - * length l into array of type T2 values. If T1 is the same as T2, copy - * operation is performed. When copying data at overlapping address spaces, - * "op" should be lower than "ip". - * - * @param ip Input buffer. - * @param[out] op Output buffer. - * @param l The number of elements to copy. - * @param ip Input buffer pointer increment. - * @param op Output buffer pointer increment. - */ - -template< class T1, class T2 > -inline void copyArray( const T1* ip, T2* op, int l, - const int ipinc = 1, const int opinc = 1 ) -{ - while( l > 0 ) - { - *op = (T2) *ip; - op += opinc; - ip += ipinc; - l--; - } -} - -/** - * Function adds values located in array "ip" to array "op". - * - * @param ip Input buffer. - * @param[out] op Output buffer. - * @param l The number of elements to add. - * @param ip Input buffer pointer increment. - * @param op Output buffer pointer increment. - */ - -template< class T1, class T2 > -inline void addArray( const T1* ip, T2* op, int l, - const int ipinc = 1, const int opinc = 1 ) -{ - while( l > 0 ) - { - *op += *ip; - op += opinc; - ip += ipinc; - l--; - } -} - -/** - * Function that replicates a set of adjacent elements several times in a row. - * This operation is usually used to replicate pixels at the start or end of - * image's scanline. - * - * @param ip Source array. - * @param ipl Source array length (usually 1..4, but can be any number). - * @param[out] op Destination buffer. - * @param l Number of times the source array should be replicated (the - * destination buffer should be able to hold ipl * l number of elements). - * @param opinc Destination buffer position increment after replicating the - * source array. This value should be equal to at least ipl. - */ - -template< class T1, class T2 > -inline void replicateArray( const T1* const ip, const int ipl, T2* op, int l, - const int opinc ) -{ - if( ipl == 1 ) - { - while( l > 0 ) - { - op[ 0 ] = (T2) ip[ 0 ]; - op += opinc; - l--; - } - } - else - if( ipl == 4 ) - { - while( l > 0 ) - { - op[ 0 ] = (T2) ip[ 0 ]; - op[ 1 ] = (T2) ip[ 1 ]; - op[ 2 ] = (T2) ip[ 2 ]; - op[ 3 ] = (T2) ip[ 3 ]; - op += opinc; - l--; - } - } - else - if( ipl == 3 ) - { - while( l > 0 ) - { - op[ 0 ] = (T2) ip[ 0 ]; - op[ 1 ] = (T2) ip[ 1 ]; - op[ 2 ] = (T2) ip[ 2 ]; - op += opinc; - l--; - } - } - else - if( ipl == 2 ) - { - while( l > 0 ) - { - op[ 0 ] = (T2) ip[ 0 ]; - op[ 1 ] = (T2) ip[ 1 ]; - op += opinc; - l--; - } - } - else - { - while( l > 0 ) - { - int i; - - for( i = 0; i < ipl; i++ ) - { - op[ i ] = (T2) ip[ i ]; - } - - op += opinc; - l--; - } - } -} - -/** - * Function calculates frequency response of the specified FIR filter at the - * specified circular frequency. Phase can be calculated as atan2( im, re ). - * Function uses computationally-efficient oscillators instead of "cos" and - * "sin" functions. - * - * @param flt FIR filter's coefficients. - * @param fltlen Number of coefficients (taps) in the filter. - * @param th Circular frequency [0; pi]. - * @param[out] re0 Resulting real part of the complex frequency response. - * @param[out] im0 Resulting imaginary part of the complex frequency response. - * @param fltlat Filter's latency in samples (taps). - */ - -template< class T > -inline void calcFIRFilterResponse( const T* flt, int fltlen, - const double th, double& re0, double& im0, const int fltlat = 0 ) -{ - const double sincr = 2.0 * cos( th ); - double cvalue1; - double svalue1; - - if( fltlat == 0 ) - { - cvalue1 = 1.0; - svalue1 = 0.0; - } - else - { - cvalue1 = cos( -fltlat * th ); - svalue1 = sin( -fltlat * th ); - } - - double cvalue2 = cos( -( fltlat + 1 ) * th ); - double svalue2 = sin( -( fltlat + 1 ) * th ); - - double re = 0.0; - double im = 0.0; - - while( fltlen > 0 ) - { - re += cvalue1 * flt[ 0 ]; - im += svalue1 * flt[ 0 ]; - flt++; - fltlen--; - - double tmp = cvalue1; - cvalue1 = sincr * cvalue1 - cvalue2; - cvalue2 = tmp; - - tmp = svalue1; - svalue1 = sincr * svalue1 - svalue2; - svalue2 = tmp; - } - - re0 = re; - im0 = im; -} - -/** - * Function normalizes FIR filter so that its frequency response at DC is - * equal to DCGain. - * - * @param[in,out] p Filter coefficients. - * @param l Filter length. - * @param DCGain Filter's gain at DC. - * @param pstep "p" array step. - */ - -template< class T > -inline void normalizeFIRFilter( T* const p, const int l, const double DCGain, - const int pstep = 1 ) -{ - double s = 0.0; - T* pp = p; - int i = l; - - while( i > 0 ) - { - s += *pp; - pp += pstep; - i--; - } - - s = DCGain / s; - pp = p; - i = l; - - while( i > 0 ) - { - *pp = (T) ( *pp * s ); - pp += pstep; - i--; - } -} - -/** - * @brief Memory buffer class for element array storage, with capacity - * tracking. - * - * Allows easier handling of memory blocks allocation and automatic - * deallocation for arrays (buffers) consisting of elements of specified - * class. Tracks buffer's capacity in "int" variable; unsuitable for - * allocation of very large memory blocks (with more than 2 billion elements). - * - * This class manages memory space only - it does not perform element class - * construction (initialization) operations. Buffer's required memory address - * alignment specification is supported. - * - * Uses standard library to allocate and deallocate memory. - * - * @tparam T Buffer element's type. - * @tparam capint Buffer capacity's type to use. Use size_t for large buffers. - */ - -template< class T, typename capint = int > -class CBuffer -{ -public: - CBuffer() - : Data( NULL ) - , DataAligned( NULL ) - , Capacity( 0 ) - , Alignment( 0 ) - { - } - - /** - * Constructor creates the buffer with the specified capacity. - * - * @param aCapacity Buffer's capacity. - * @param aAlignment Buffer's required memory address alignment. 0 - use - * stdlib's default alignment. - */ - - CBuffer( const capint aCapacity, const int aAlignment = 0 ) - { - allocinit( aCapacity, aAlignment ); - } - - CBuffer( const CBuffer& Source ) - { - allocinit( Source.Capacity, Source.Alignment ); - - if( Capacity > 0 ) - { - memcpy( DataAligned, Source.DataAligned, Capacity * sizeof( T )); - } - } - - ~CBuffer() - { - freeData(); - } - - CBuffer& operator = ( const CBuffer& Source ) - { - alloc( Source.Capacity, Source.Alignment ); - - if( Capacity > 0 ) - { - memcpy( DataAligned, Source.DataAligned, Capacity * sizeof( T )); - } - - return( *this ); - } - - /** - * Function allocates memory so that the specified number of elements - * can be stored in *this buffer object. - * - * @param aCapacity Storage for this number of elements to allocate. - * @param aAlignment Buffer's required memory address alignment, - * power-of-2 values only. 0 - use stdlib's default alignment. - */ - - void alloc( const capint aCapacity, const int aAlignment = 0 ) - { - freeData(); - allocinit( aCapacity, aAlignment ); - } - - /** - * Function deallocates any previously allocated buffer. - */ - - void free() - { - freeData(); - Data = NULL; - DataAligned = NULL; - Capacity = 0; - Alignment = 0; - } - - /** - * @return The capacity of the element buffer. - */ - - capint getCapacity() const - { - return( Capacity ); - } - - /** - * Function "forces" *this buffer to have an arbitary capacity. Calling - * this function invalidates all further operations except deleting *this - * object. This function should not be usually used at all. Function can - * be used to "model" certain buffer capacity without calling a costly - * memory allocation function. - * - * @param NewCapacity A new "forced" capacity. - */ - - void forceCapacity( const capint NewCapacity ) - { - Capacity = NewCapacity; - } - - /** - * Function reallocates *this buffer to a larger size so that it will be - * able to hold the specified number of elements. Downsizing is not - * performed. Alignment is not changed. - * - * @param NewCapacity New (increased) capacity. - * @param DoDataCopy "True" if data in the buffer should be retained. - */ - - void increaseCapacity( const capint NewCapacity, - const bool DoDataCopy = true ) - { - if( NewCapacity < Capacity ) - { - return; - } - - if( DoDataCopy ) - { - const capint PrevCapacity = Capacity; - T* const PrevData = Data; - T* const PrevDataAligned = DataAligned; - - allocinit( NewCapacity, Alignment ); - - if( PrevCapacity > 0 ) - { - memcpy( DataAligned, PrevDataAligned, - PrevCapacity * sizeof( T )); - } - - :: free( PrevData ); - } - else - { - :: free( Data ); - allocinit( NewCapacity, Alignment ); - } - } - - /** - * Function "truncates" (reduces) capacity of the buffer without - * reallocating it. Alignment is not changed. - * - * @param NewCapacity New required capacity. - */ - - void truncateCapacity( const capint NewCapacity ) - { - if( NewCapacity >= Capacity ) - { - return; - } - - Capacity = NewCapacity; - } - - /** - * Function increases capacity so that the specified number of - * elements can be stored. This function increases the previous capacity - * value by third the current capacity value until space for the required - * number of elements is available. Alignment is not changed. - * - * @param ReqCapacity Required capacity. - */ - - void updateCapacity( const capint ReqCapacity ) - { - if( ReqCapacity <= Capacity ) - { - return; - } - - capint NewCapacity = Capacity; - - while( NewCapacity < ReqCapacity ) - { - NewCapacity += NewCapacity / 3 + 1; - } - - increaseCapacity( NewCapacity ); - } - - operator T* () const - { - return( DataAligned ); - } - -private: - T* Data; ///< Element buffer pointer. - ///< - T* DataAligned; ///< Memory address-aligned element buffer pointer. - ///< - capint Capacity; ///< Element buffer capacity. - ///< - int Alignment; ///< Memory address alignment in use. 0 - use stdlib's - ///< default alignment. - ///< - - /** - * Internal element buffer allocation function used during object - * construction. - * - * @param aCapacity Storage for this number of elements to allocate. - * @param aAlignment Buffer's required memory address alignment. 0 - use - * stdlib's default alignment. - */ - - void allocinit( const capint aCapacity, const int aAlignment ) - { - if( aAlignment == 0 ) - { - Data = (T*) :: malloc( aCapacity * sizeof( T )); - DataAligned = Data; - Alignment = 0; - } - else - { - Data = (T*) :: malloc( aCapacity * sizeof( T ) + aAlignment ); - DataAligned = alignptr( Data, aAlignment ); - Alignment = aAlignment; - } - - Capacity = aCapacity; - } - - /** - * Function frees a previously allocated Data buffer. - */ - - void freeData() - { - :: free( Data ); - } - - /** - * Function modifies the specified pointer so that it becomes memory - * address-aligned. - * - * @param ptr Pointer to align. - * @param align Alignment in bytes to apply. - * @return Pointer aligned to align bytes. Works with power-of-2 - * alignments only. If no alignment is necessary, "align" bytes will be - * added to the pointer value. - */ - - template< class Tp > - inline Tp alignptr( const Tp ptr, const uintptr_t align ) - { - return( (Tp) ( (uintptr_t) ptr + align - - ( (uintptr_t) ptr & ( align - 1 ))) ); - } -}; - -/** - * @brief Array of structured objects. - * - * Implements allocation of a linear array of objects of class T (which are - * initialized), addressable via operator[]. Each object is created via the - * "operator new". New object insertions are quick since implementation uses - * prior space allocation (capacity), thus not requiring frequent memory block - * reallocations. - * - * @tparam T Array element's type. - */ - -template< class T > -class CStructArray -{ -public: - CStructArray() - : ItemCount( 0 ) - { - } - - CStructArray( const CStructArray& Source ) - : ItemCount( 0 ) - , Items( Source.getItemCount() ) - { - while( ItemCount < Source.getItemCount() ) - { - Items[ ItemCount ] = new T( Source[ ItemCount ]); - ItemCount++; - } - } - - ~CStructArray() - { - clear(); - } - - CStructArray& operator = ( const CStructArray& Source ) - { - clear(); - - const int NewCount = Source.ItemCount; - Items.updateCapacity( NewCount ); - - while( ItemCount < NewCount ) - { - Items[ ItemCount ] = new T( Source[ ItemCount ]); - ItemCount++; - } - - return( *this ); - } - - T& operator []( const int Index ) - { - return( *Items[ Index ]); - } - - const T& operator []( const int Index ) const - { - return( *Items[ Index ]); - } - - /** - * Function creates a new object of type T with the default constructor - * and adds this object to the array. - * - * @return Reference to a newly added object. - */ - - T& add() - { - if( ItemCount == Items.getCapacity() ) - { - Items.increaseCapacity( ItemCount * 3 / 2 + 1 ); - } - - Items[ ItemCount ] = new T(); - ItemCount++; - - return( (*this)[ ItemCount - 1 ]); - } - - /** - * Function changes number of allocated items. New items are created with - * the default constructor. If NewCount is below the current item count, - * items that are above NewCount range will be destructed. - * - * @param NewCount New requested item count. - */ - - void setItemCount( const int NewCount ) - { - if( NewCount > ItemCount ) - { - Items.increaseCapacity( NewCount ); - - while( ItemCount < NewCount ) - { - Items[ ItemCount ] = new T(); - ItemCount++; - } - } - else - { - while( ItemCount > NewCount ) - { - ItemCount--; - delete Items[ ItemCount ]; - } - } - } - - /** - * Function erases all items of *this array. - */ - - void clear() - { - while( ItemCount > 0 ) - { - ItemCount--; - delete Items[ ItemCount ]; - } - } - - /** - * @return The number of allocated items. - */ - - int getItemCount() const - { - return( ItemCount ); - } - -private: - int ItemCount; ///< The number of items available in the array. - ///< - CBuffer< T* > Items; ///< Element buffer. - ///< -}; - -/** - * @brief Sine signal generator class. - * - * Class implements sine signal generator without biasing, with - * constructor-based initalization only. This generator uses oscillator - * instead of "sin" function. - */ - -class CSineGen -{ -public: - /** - * Constructor initializes *this sine signal generator. - * - * @param si Sine function increment, in radians. - * @param ph Starting phase, in radians. Add 0.5 * AVIR_PI for cosine - * function. - */ - - CSineGen( const double si, const double ph ) - : svalue1( sin( ph )) - , svalue2( sin( ph - si )) - , sincr( 2.0 * cos( si )) - { - } - - /** - * @return The next value of the sine function, without biasing. - */ - - double generate() - { - const double res = svalue1; - - svalue1 = sincr * res - svalue2; - svalue2 = res; - - return( res ); - } - -private: - double svalue1; ///< Current sine value. - ///< - double svalue2; ///< Previous sine value. - ///< - double sincr; ///< Sine value increment. - ///< -}; - -/** - * @brief Peaked Cosine window function generator class. - * - * Class implements Peaked Cosine window function generator. Generates the - * right-handed half of the window function. The Alpha parameter of this - * window function offers the control of the balance between the early and - * later taps of the filter. E.g. at Alpha=1 both early and later taps are - * attenuated, but at Alpha=4 mostly later taps are attenuated. This offers a - * great control over ringing artifacts produced by a low-pass filter in image - * processing, without compromising achieved image sharpness. - */ - -class CDSPWindowGenPeakedCosine -{ -public: - /** - * Constructor initializes *this window function generator. - * - * @param aAlpha Alpha parameter, affects the peak shape (peak - * augmentation) of the window function. Any positive value can be used. - * @param aLen2 Half filter's length (non-truncated). - */ - - CDSPWindowGenPeakedCosine( const double aAlpha, const double aLen2 ) - : Alpha( aAlpha ) - , Len2( aLen2 ) - , Len2i( 1.0 / aLen2 ) - , wn( 0.0 ) - , w1( AVIR_PId2 / Len2, AVIR_PI * 0.5 ) - { - } - - /** - * @return The next Peaked Cosine window function coefficient. - */ - - double generate() - { - const double h = pow( wn * Len2i, Alpha ); - wn += 1.0; - - return( w1.generate() * ( 1.0 - h )); - } - -private: - double Alpha; ///< Alpha parameter, affects the peak shape of window. - ///< - double Len2; ///< Half length of the window function. - ///< - double Len2i; ///< = 1 / Len2. - ///< - double wn; ///< Window function integer position. 0 - center of the - ///< window function. - ///< - CSineGen w1; ///< Sine-wave generator. - ///< -}; - -/** - * @brief FIR filter-based equalizer generator. - * - * Class implements an object used to generate symmetric-odd FIR filters with - * the specified frequency response (aka paragraphic equalizer). The - * calculated filter is windowed by the Peaked Cosine window function. - * - * In image processing, due to short length of filters being used (6-8 taps) - * the resulting frequency response of the filter is approximate and may be - * mathematically imperfect, but still adequate to the visual requirements. - * - * On a side note, this equalizer generator can be successfully used for audio - * signal equalization as well: for example, it is used in almost the same - * form in Voxengo Marvel GEQ equalizer plug-in. - * - * Filter generation is based on decomposition of frequency range into - * spectral bands, with each band represented by linear and ramp "kernels". - * When the filter is built, these kernels are combined together with - * different weights that approximate the required frequency response. - */ - -class CDSPFIREQ -{ -public: - /** - * Function initializes *this object with the required parameters. The - * gain of frequencies beyond the MinFreq..MaxFreq range are controlled by - * the first and the last band's gain. - * - * @param SampleRate Processing sample rate (use 2 for image processing). - * @param aFilterLength Required filter length in samples (taps). The - * actual filter length is truncated to an integer value. - * @param aBandCount Number of band crossover points required to control, - * including bands at MinFreq and MaxFreq. - * @param MinFreq Minimal frequency that should be controlled. - * @param MaxFreq Maximal frequency that should be controlled. - * @param IsLogBands "True" if the bands should be spaced logarithmically. - * @param WFAlpha Peaked Cosine window function's Alpha parameter. - */ - - void init( const double SampleRate, const double aFilterLength, - const int aBandCount, const double MinFreq, const double MaxFreq, - const bool IsLogBands, const double WFAlpha ) - { - FilterLength = aFilterLength; - BandCount = aBandCount; - - CenterFreqs.alloc( BandCount ); - - z = (int) ceil( FilterLength * 0.5 ); - zi = z + ( z & 1 ); - z2 = z * 2; - - CBuffer< double > oscbuf( z2 ); - initOscBuf( oscbuf ); - - CBuffer< double > winbuf( z ); - initWinBuf( winbuf, WFAlpha ); - - UseFirstVirtBand = ( MinFreq > 0.0 ); - const int k = zi * ( BandCount + ( UseFirstVirtBand ? 1 : 0 )); - Kernels1.alloc( k ); - Kernels2.alloc( k ); - - double m; // Frequency step multiplier. - double mo; // Frequency step offset (addition). - - if( IsLogBands ) - { - m = exp( log( MaxFreq / MinFreq ) / ( BandCount - 1 )); - mo = 0.0; - } - else - { - m = 1.0; - mo = ( MaxFreq - MinFreq ) / ( BandCount - 1 ); - } - - double f = MinFreq; - double x1 = 0.0; - double x2; - int si; - - if( UseFirstVirtBand ) - { - si = 0; - } - else - { - si = 1; - CenterFreqs[ 0 ] = 0.0; - f = f * m + mo; - } - - double* kernbuf1 = &Kernels1[ 0 ]; - double* kernbuf2 = &Kernels2[ 0 ]; - int i; - - for( i = si; i < BandCount; i++ ) - { - x2 = f * 2.0 / SampleRate; - CenterFreqs[ i ] = x2; - - fillBandKernel( x1, x2, kernbuf1, kernbuf2, oscbuf, winbuf ); - - kernbuf1 += zi; - kernbuf2 += zi; - x1 = x2; - f = f * m + mo; - } - - if( x1 < 1.0 ) - { - UseLastVirtBand = true; - fillBandKernel( x1, 1.0, kernbuf1, kernbuf2, oscbuf, winbuf ); - } - else - { - UseLastVirtBand = false; - } - } - - /** - * @return Filter's length, in samples (taps). - */ - - int getFilterLength() const - { - return( z2 - 1 ); - } - - /** - * @return Filter's latency (group delay), in samples (taps). - */ - - int getFilterLatency() const - { - return( z - 1 ); - } - - /** - * Function creates symmetric-odd FIR filter with the specified gain - * levels at band crossover points. - * - * @param BandGains Array of linear gain levels, count=BandCount specified - * in the init() function. - * @param[out] Filter Output filter buffer, length = getFilterLength(). - */ - - void buildFilter( const double* const BandGains, double* const Filter ) - { - const double* kernbuf1 = &Kernels1[ 0 ]; - const double* kernbuf2 = &Kernels2[ 0 ]; - double x1 = 0.0; - double y1 = BandGains[ 0 ]; - double x2; - double y2; - - int i; - int si; - - if( UseFirstVirtBand ) - { - si = 1; - x2 = CenterFreqs[ 0 ]; - y2 = y1; - } - else - { - si = 2; - x2 = CenterFreqs[ 1 ]; - y2 = BandGains[ 1 ]; - } - - copyBandKernel( Filter, kernbuf1, kernbuf2, y1 - y2, - x1 * y2 - x2 * y1 ); - - kernbuf1 += zi; - kernbuf2 += zi; - x1 = x2; - y1 = y2; - - for( i = si; i < BandCount; i++ ) - { - x2 = CenterFreqs[ i ]; - y2 = BandGains[ i ]; - - addBandKernel( Filter, kernbuf1, kernbuf2, y1 - y2, - x1 * y2 - x2 * y1 ); - - kernbuf1 += zi; - kernbuf2 += zi; - x1 = x2; - y1 = y2; - } - - if( UseLastVirtBand ) - { - addBandKernel( Filter, kernbuf1, kernbuf2, y1 - y2, - x1 * y2 - y1 ); - } - - for( i = 0; i < z - 1; i++ ) - { - Filter[ z + i ] = Filter[ z - 2 - i ]; - } - } - - /** - * Function calculates filter's length (in samples) and latency depending - * on the required non-truncated filter length. - * - * @param aFilterLength Required filter length in samples (non-truncated). - * @param[out] Latency Resulting latency (group delay) of the filter, - * in samples (taps). - * @return Filter length in samples (taps). - */ - - static int calcFilterLength( const double aFilterLength, int& Latency ) - { - const int l = (int) ceil( aFilterLength * 0.5 ); - Latency = l - 1; - - return( l * 2 - 1 ); - } - -private: - double FilterLength; ///< Length of filter. - ///< - int z; ///< Equals (int) ceil( FilterLength * 0.5 ). - ///< - int zi; ///< Equals "z" if z is even, or z + 1 if z is odd. Used as a - ///< Kernels1 and Kernels2 size multiplier and kernel buffer increment - ///< to make sure each kernel buffer is 16-byte aligned. - ///< - int z2; ///< Equals z * 2. - ///< - int BandCount; ///< Number of controllable bands. - ///< - CBuffer< double > CenterFreqs; ///< Center frequencies for all bands, - ///< normalized to 0.0-1.0 range. - ///< - CBuffer< double > Kernels1; ///< Half-length kernel buffers for each - ///< spectral band (linear part). - ///< - CBuffer< double > Kernels2; ///< Half-length kernel buffers for each - ///< spectral band (ramp part). - ///< - bool UseFirstVirtBand; ///< "True" if the first virtual band - ///< (between 0.0 and MinFreq) should be used. The first virtual band - ///< won't be used if MinFreq equals 0.0. - ///< - bool UseLastVirtBand; ///< "True" if the last virtual band (between - ///< MaxFreq and SampleRate * 0.5) should be used. The last virtual - ///< band won't be used if MaxFreq * 2.0 equals SampleRate. - ///< - - /** - * Function initializes the "oscbuf" used in the fillBandKernel() - * function. - * - * @param oscbuf Oscillator buffer, length = z * 2. - */ - - void initOscBuf( double* oscbuf ) const - { - int i = z; - - while( i > 0 ) - { - oscbuf[ 0 ] = 0.0; - oscbuf[ 1 ] = 1.0; - oscbuf += 2; - i--; - } - } - - /** - * Function initializes window function buffer. This function generates - * Peaked Cosine window function. - * - * @param winbuf Windowing buffer. - * @param Alpha Peaked Cosine alpha parameter. - */ - - void initWinBuf( double* winbuf, const double Alpha ) const - { - CDSPWindowGenPeakedCosine wf( Alpha, FilterLength * 0.5 ); - int i; - - for( i = 1; i <= z; i++ ) - { - winbuf[ z - i ] = wf.generate(); - } - } - - /** - * Function fills first half of symmetric-odd FIR kernel for the band. - * This function should be called successively for adjacent bands. - * Previous band's x2 should be equal to current band's x1. A band kernel - * consists of 2 elements: linear kernel and ramp kernel. - * - * @param x1 Band's left corner frequency (0..1). - * @param x2 Band's right corner frequency (0..1). - * @param kernbuf1 Band kernel buffer 1 (linear part), length = z. - * @param kernbuf2 Band kernel buffer 2 (ramp part), length = z. - * @param oscbuf Oscillation buffer. Before the first call of the - * fillBandKernel() should be initialized with the call of the - * initOscBuf() function. - * @param winbuf Buffer that contains windowing function. - */ - - void fillBandKernel( const double x1, const double x2, double* kernbuf1, - double* kernbuf2, double* oscbuf, const double* const winbuf ) - { - const double s2_incr = AVIR_PI * x2; - const double s2_coeff = 2.0 * cos( s2_incr ); - - double s2_value1 = sin( s2_incr * ( -z + 1 )); - double c2_value1 = sin( s2_incr * ( -z + 1 ) + AVIR_PI * 0.5 ); - oscbuf[ 0 ] = sin( s2_incr * -z ); - oscbuf[ 1 ] = sin( s2_incr * -z + AVIR_PI * 0.5 ); - - int ks; - - for( ks = 1; ks < z; ks++ ) - { - const int ks2 = ks * 2; - const double s1_value1 = oscbuf[ ks2 ]; - const double c1_value1 = oscbuf[ ks2 + 1 ]; - oscbuf[ ks2 ] = s2_value1; - oscbuf[ ks2 + 1 ] = c2_value1; - - const double x = AVIR_PI * ( ks - z ); - const double v0 = winbuf[ ks - 1 ] / (( x1 - x2 ) * x ); - - kernbuf1[ ks - 1 ] = ( x2 * s2_value1 - x1 * s1_value1 + - ( c2_value1 - c1_value1 ) / x ) * v0; - - kernbuf2[ ks - 1 ] = ( s2_value1 - s1_value1 ) * v0; - - s2_value1 = s2_coeff * s2_value1 - oscbuf[ ks2 - 2 ]; - c2_value1 = s2_coeff * c2_value1 - oscbuf[ ks2 - 1 ]; - } - - kernbuf1[ z - 1 ] = ( x2 * x2 - x1 * x1 ) / ( x1 - x2 ) * 0.5; - kernbuf2[ z - 1 ] = -1.0; - } - - /** - * Function copies band kernel's elements to the output buffer. - * - * @param outbuf Output buffer. - * @param kernbuf1 Kernel buffer 1 (linear part). - * @param kernbuf2 Kernel buffer 2 (ramp part). - * @param c Multiplier for linear kernel element. - * @param d Multiplier for ramp kernel element. - */ - - void copyBandKernel( double* outbuf, const double* const kernbuf1, - const double* const kernbuf2, const double c, const double d ) const - { - int ks; - - for( ks = 0; ks < z; ks++ ) - { - outbuf[ ks ] = c * kernbuf1[ ks ] + d * kernbuf2[ ks ]; - } - } - - /** - * Function adds band kernel's elements to the output buffer. - * - * @param outbuf Output buffer. - * @param kernbuf1 Kernel buffer 1 (linear part). - * @param kernbuf2 Kernel buffer 2 (ramp part). - * @param c Multiplier for linear kernel element. - * @param d Multiplier for ramp kernel element. - */ - - void addBandKernel( double* outbuf, const double* const kernbuf1, - const double* const kernbuf2, const double c, const double d ) const - { - int ks; - - for( ks = 0; ks < z; ks++ ) - { - outbuf[ ks ] += c * kernbuf1[ ks ] + d * kernbuf2[ ks ]; - } - } -}; - -/** - * @brief Low-pass filter windowed by Peaked Cosine window function. - * - * This class implements calculation of linear-phase symmetric-odd FIR - * low-pass filter windowed by the Peaked Cosine window function, for image - * processing applications. - */ - -class CDSPPeakedCosineLPF -{ -public: - int fl2; ///< Half filter's length, excluding the peak value. This value - ///< can be also used as filter's latency in samples (taps). - ///< - int FilterLen; ///< Filter's length in samples (taps). - ///< - - /** - * Constructor initalizes *this object. - * - * @param aLen2 Half-length (non-truncated) of low-pass filter, in samples - * (taps). - * @param aFreq2 Low-pass filter's corner frequency [0; pi]. - * @param aAlpha Peaked Cosine window function Alpha parameter. - */ - - CDSPPeakedCosineLPF( const double aLen2, const double aFreq2, - const double aAlpha ) - : fl2( (int) ceil( aLen2 ) - 1 ) - , FilterLen( fl2 + fl2 + 1 ) - , Len2( aLen2 ) - , Freq2( aFreq2 ) - , Alpha( aAlpha ) - { - } - - /** - * Function generates a linear-phase low-pass filter windowed by Peaked - * Cosine window function. - * - * @param[out] op Output buffer, length = FilterLen (fl2 * 2 + 1). - * @param DCGain Required gain at DC. The resulting filter will be - * normalized to achieve this DC gain. If non-positive, no automatic - * normalization will be performed. - */ - - template< class T > - void generateLPF( T* op, const double DCGain ) - { - CDSPWindowGenPeakedCosine wf( Alpha, Len2 ); - CSineGen f2( Freq2, 0.0 ); - - op += fl2; - T* op2 = op; - f2.generate(); - - if( DCGain > 0.0 ) - { - int t = 1; - - *op = (T) ( Freq2 * wf.generate() ); - double s = *op; - - while( t <= fl2 ) - { - const T v = (T) ( f2.generate() * wf.generate() / t ); - op++; - op2--; - *op = v; - *op2 = v; - s += v + v; - t++; - } - - t = FilterLen; - s = DCGain / s; - - while( t > 0 ) - { - *op2 = (T) ( *op2 * s ); - op2++; - t--; - } - } - else - { - int t = 1; - - *op = (T) ( Freq2 * wf.generate() ); - - while( t <= fl2 ) - { - const T v = (T) ( f2.generate() * wf.generate() / t ); - op++; - op2--; - *op = v; - *op2 = v; - t++; - } - } - } - -private: - double Len2; ///< Half-length (non-truncated) of low-pass filter, in - ///< samples (taps). - ///< - double Freq2; ///< Low-pass filter's corner frequency. - ///< - double Alpha; ///< Peaked Cosine window function Alpha parameter. - ///< -}; - -/** - * @brief Buffer class for parametrized low-pass filter. - * - * This class extends the CBuffer< double > class by adding several variables - * that define a symmetric-odd FIR low-pass filter windowed by Peaked Cosine - * window function. This class can be used to compare filters without - * comparing their buffer contents. - */ - -class CFltBuffer : public CBuffer< double > -{ -public: - double Len2; ///< Half-length (non-truncated) of low-pass filters, in - ///< samples (taps). - ///< - double Freq; ///< Low-pass filter's corner frequency. - ///< - double Alpha; ///< Peaked Cosine window function Alpha parameter. - ///< - double DCGain; ///< DC gain applied to the filter. - ///< - - CFltBuffer() - : CBuffer< double >() - , Len2( 0.0 ) - , Freq( 0.0 ) - , Alpha( 0.0 ) - , DCGain( 0.0 ) - { - } - - /** - * @param b2 Filter buffer to compare *this object to. - * @return Operator returns "true" if both filters have same parameters. - */ - - bool operator == ( const CFltBuffer& b2 ) const - { - return( Len2 == b2.Len2 && Freq == b2.Freq && Alpha == b2.Alpha && - DCGain == b2.DCGain ); - } -}; - -/** - * @brief Sinc function-based fractional delay filter bank. - * - * Class implements storage and initialization of a bank of sinc - * function-based fractional delay filters, expressed as 1st order polynomial - * interpolation coefficients. The filters are produced from a single "long" - * windowed low-pass filter. Also supports 0th-order ("nearest neighbor") - * interpolation. - * - * This class also supports multiplication of each fractional delay filter by - * an external filter (usually a low-pass filter). - * - * @tparam fptype Specifies storage type of the filter coefficients bank. The - * filters are initially calculated using the "double" precision. - */ - -template< class fptype > -class CDSPFracFilterBankLin -{ - AVIR_NOCTOR( CDSPFracFilterBankLin ); - -public: - CDSPFracFilterBankLin() - : Order( -1 ) - { - } - - /** - * Copy constructor copies a limited set of parameters of the source - * filter bank. The actual filters are not copied. Such copying is used - * during filtering steps "modeling" stage. A further init() function - * call is required. - * - * @param s Source filter bank. - */ - - void copyInitParams( const CDSPFracFilterBankLin& s ) - { - WFLen2 = s.WFLen2; - WFFreq = s.WFFreq; - WFAlpha = s.WFAlpha; - FracCount = s.FracCount; - Order = s.Order; - Alignment = s.Alignment; - SrcFilterLen = s.SrcFilterLen; - FilterLen = s.FilterLen; - FilterSize = s.FilterSize; - IsSrcTableBuilt = false; - ExtFilter = s.ExtFilter; - TableFillFlags.alloc( s.TableFillFlags.getCapacity() ); - int i; - - // Copy table fill flags, but shifted so that further initialization - // is still possible (such feature should not be used, though). - - for( i = 0; i < TableFillFlags.getCapacity(); i++ ) - { - TableFillFlags[ i ] = (uint8_t) ( s.TableFillFlags[ i ] << 2 ); - } - } - - /** - * Operator compares *this filter bank and another filter bank and returns - * "true" if their parameters are equal. Alignment is not taken into - * account. - * - * @param s Filter bank to compare to. - * @return "True" if compared banks have equal parameters. - */ - - bool operator == ( const CDSPFracFilterBankLin& s ) const - { - return( Order == s.Order && WFLen2 == s.WFLen2 && - WFFreq == s.WFFreq && WFAlpha == s.WFAlpha && - FracCount == s.FracCount && ExtFilter == s.ExtFilter ); - } - - /** - * Function initializes (builds) the filter bank based on the supplied - * parameters. If the supplied parameters are equal to previously defined - * parameters, function does nothing (alignment is assumed to be never - * changing between the init() function calls). - * - * @param ReqFracCount Required number of fractional delays in the filter - * bank. The minimal value is 2. - * @param ReqOrder Required order of the interpolation polynomial - * (0 or 1). - * @param BaseLen Low-pass filter's base length, in samples (taps). - * Affects the actual length of the filter and its overall steepness. - * @param Cutoff Low-pass filter's normalized cutoff frequency [0; 1]. - * @param aWFAlpha Peaked Cosine window function's Alpha parameter. - * @param aExtFilter External filter to apply to each fractional delay - * filter. - * @param aAlignment Memory alignment of the filter bank, power-of-2 - * value. 0 - use default stdlib alignment. - * @param FltLenAlign Filter's length alignment, power-of-2 value. - */ - - void init( const int ReqFracCount, const int ReqOrder, - const double BaseLen, const double Cutoff, const double aWFAlpha, - const CFltBuffer& aExtFilter, const int aAlignment = 0, - const int FltLenAlign = 1 ) - { - double NewWFLen2 = 0.5 * BaseLen * ReqFracCount; - double NewWFFreq = AVIR_PI * Cutoff / ReqFracCount; - double NewWFAlpha = aWFAlpha; - - if( ReqOrder == Order && NewWFLen2 == WFLen2 && NewWFFreq == WFFreq && - NewWFAlpha == WFAlpha && ReqFracCount == FracCount && - aExtFilter == ExtFilter ) - { - IsInitRequired = false; - return; - } - - WFLen2 = NewWFLen2; - WFFreq = NewWFFreq; - WFAlpha = NewWFAlpha; - FracCount = ReqFracCount; - Order = ReqOrder; - Alignment = aAlignment; - ExtFilter = aExtFilter; - - CDSPPeakedCosineLPF p( WFLen2, WFFreq, WFAlpha ); - SrcFilterLen = ( p.fl2 / ReqFracCount + 1 ) * 2; - - const int ElementSize = ReqOrder + 1; - FilterLen = SrcFilterLen; - - if( ExtFilter.getCapacity() > 0 ) - { - FilterLen += ExtFilter.getCapacity() - 1; - } - - FilterLen = ( FilterLen + FltLenAlign - 1 ) & ~( FltLenAlign - 1 ); - FilterSize = FilterLen * ElementSize; - IsSrcTableBuilt = false; - IsInitRequired = true; - } - - /** - * @return The length of each fractional delay filter, in samples (taps). - * Always an even value. - */ - - int getFilterLen() const - { - return( FilterLen ); - } - - /** - * @return The number of fractional filters in use by *this bank. - */ - - int getFracCount() const - { - return( FracCount ); - } - - /** - * @return The order of the interpolation polynomial. - */ - - int getOrder() const - { - return( Order ); - } - - /** - * Function returns the pointer to the specified interpolation table - * filter. - * - * @param i Filter (fractional delay) index, in the range 0 to - * ReqFracCount - 1, inclusive. - * @return Pointer to filter. Higher order polynomial coefficients are - * stored after after previous order coefficients, separated by FilterLen - * elements. - */ - - const fptype* getFilter( const int i ) - { - if( !IsSrcTableBuilt ) - { - buildSrcTable(); - } - - fptype* const Res = &Table[ i * FilterSize ]; - - if(( TableFillFlags[ i ] & 2 ) == 0 ) - { - createFilter( i ); - TableFillFlags[ i ] |= 2; - - if( Order > 0 ) - { - createFilter( i + 1 ); - const fptype* const Res2 = Res + FilterSize; - fptype* const op = Res + FilterLen; - int j; - - // Create higher-order interpolation coefficients (linear - // interpolation). - - for( j = 0; j < FilterLen; j++ ) - { - op[ j ] = Res2[ j ] - Res[ j ]; - } - } - } - - return( Res ); - } - - /** - * Function makes sure all fractional delay filters were created. - */ - - void createAllFilters() - { - int i; - - for( i = 0; i < FracCount; i++ ) - { - getFilter( i ); - } - } - - /** - * Function returns an approximate initialization complexity, expressed in - * the number of multiply-add operations. This includes fractional delay - * filters calculation and multiplication by an external filter. This - * function can only be called after the init() function. - * - * @param FracUseMap Fractional delays use map, each element corresponds - * to a single fractional delay, will be compared to the internal table - * fill flags. This map should include 0 and 1 values only. - * @return The complexity of the initialization, expressed in the number - * of multiply-add operations. - */ - - int calcInitComplexity( const CBuffer< uint8_t >& FracUseMap ) const - { - const int FltInitCost = 65; // Cost to initialize a single sample - // of the fractional delay filter. - const int FltUseCost = FilterLen * Order + - SrcFilterLen * ExtFilter.getCapacity(); // Cost to use a single - // fractional delay filter. - const int ucb[ 2 ] = { 0, FltUseCost }; - int ic; - int i; - - if( IsInitRequired ) - { - ic = FracCount * SrcFilterLen * FltInitCost; - - for( i = 0; i < FracCount; i++ ) - { - ic += ucb[ FracUseMap[ i ]]; - } - } - else - { - ic = 0; - - for( i = 0; i < FracCount; i++ ) - { - if( FracUseMap[ i ] != 0 ) - { - ic += ucb[ TableFillFlags[ i ] == 0 ? 1 : 0 ]; - } - } - } - - return( ic ); - } - -private: - static const int InterpPoints = 2; ///< The maximal number of points the - ///< interpolation is based on. - ///< - double WFLen2; ///< Window function's Len2 parameter. - ///< - double WFFreq; ///< Window function's Freq parameter. - ///< - double WFAlpha; ///< Window function's Alpha parameter. - ///< - int FracCount; ///< The required number of fractional delay filters. - ///< - int Order; ///< The order of the interpolation polynomial. - ///< - int Alignment; ///< The required filter table alignment. - ///< - int SrcFilterLen; ///< Length of the "source" filters. This is always an - ///< even value. - ///< - int FilterLen; ///< Specifies the number of samples (taps) each fractional - ///< delay filter has. This is always an even value, adjusted by the - ///< FltLenAlign. - ///< - int FilterSize; ///< The size of a single filter element, equals - ///< FilterLen * ElementSize. - ///< - bool IsInitRequired; ///< "True" if SrcTable filter table initialization - ///< is required. This value is available only after the call to the - ///< init() function. - ///< - CBuffer< fptype > Table; ///< Interpolation table, size equals to - ///< ReqFracCount * FilterLen * ElementSize. - ///< - CBuffer< uint8_t > TableFillFlags; ///< Contains ReqFracCount + 1 - ///< elements. Bit 0 of every element is 1 if Table already contains - ///< the filter from SrcTable filtered by ExtFilter. Bit 1 of every - ///< element means higher order coefficients were filled for the - ///< filter. - ///< - CFltBuffer ExtFilter; ///< External filter that should be applied to every - ///< fractional delay filter. Can be empty. Half of this filter's - ///< capacity is used as latency (group delay) value of the filter. - ///< - CBuffer< double > SrcTable; ///< Source table of delay filters, contains - ///< ReqFracCount + 1 elements. This table is used to fill the Table - ///< with the actual filters, filtered by an external filter. - ///< - bool IsSrcTableBuilt; ///< "True" if the SrcTable was built already. This - ///< variable is set to "false" in the init() function. - ///< - - /** - * Function builds source table used in the createFilter() function. - */ - - void buildSrcTable() - { - IsSrcTableBuilt = true; - IsInitRequired = false; - - CDSPPeakedCosineLPF p( WFLen2, WFFreq, WFAlpha ); - - const int BufLen = SrcFilterLen * FracCount + InterpPoints - 1; - const int BufOffs = InterpPoints / 2 - 1; - const int BufCenter = SrcFilterLen * FracCount / 2 + BufOffs; - - CBuffer< double > Buf( BufLen ); - memset( Buf, 0, ( BufCenter - p.fl2 ) * sizeof( double )); - int i = BufLen - BufCenter - p.fl2 - 1; - memset( &Buf[ BufLen - i ], 0, i * sizeof( double )); - - p.generateLPF( &Buf[ BufCenter - p.fl2 ], 0.0 ); - - SrcTable.alloc(( FracCount + 1 ) * SrcFilterLen ); - TableFillFlags.alloc( FracCount + 1 ); - int j; - double* op0 = SrcTable; - - for( i = FracCount; i >= 0; i-- ) - { - TableFillFlags[ i ] = 0; - double* p = Buf + BufOffs + i; - - for( j = 0; j < SrcFilterLen; j++ ) - { - op0[ 0 ] = p[ 0 ]; - op0++; - p += FracCount; - } - - normalizeFIRFilter( op0 - SrcFilterLen, SrcFilterLen, 1.0 ); - } - - Table.alloc(( FracCount + 1 ) * FilterSize, Alignment ); - } - - /** - * Function creates the specified filter in the Table by copying it from - * the SrcTable and filtering by ExtFilter. Function does nothing if - * filter was already created. - * - * @param k Filter index to create, in the range 0 to FracCount, - * inclusive. - */ - - void createFilter( const int k ) - { - if( TableFillFlags[ k ] != 0 ) - { - return; - } - - TableFillFlags[ k ] |= 1; - const int ExtFilterLatency = ExtFilter.getCapacity() / 2; - const int ResLatency = ExtFilterLatency + SrcFilterLen / 2; - int ResLen = SrcFilterLen; - - if( ExtFilter.getCapacity() > 0 ) - { - ResLen += ExtFilter.getCapacity() - 1; - } - - const int ResOffs = FilterLen / 2 - ResLatency; - fptype* op = &Table[ k * FilterSize ]; - int i; - - for( i = 0; i < ResOffs; i++ ) - { - op[ i ] = (fptype) 0; - } - - for( i = ResOffs + ResLen; i < FilterLen; i++ ) - { - op[ i ] = (fptype) 0; - } - - op += ResOffs; - const double* const srcflt = &SrcTable[ k * SrcFilterLen ]; - - if( ExtFilter.getCapacity() == 0 ) - { - for( i = 0; i < ResLen; i++ ) - { - op[ i ] = (fptype) srcflt[ i ]; - } - - return; - } - - // Perform convolution of extflt and srcflt. - - const double* const extflt = &ExtFilter[ 0 ]; - int j; - - for( j = 0; j < ResLen; j++ ) - { - int k = 0; - int l = j - ExtFilter.getCapacity() + 1; - int r = l + ExtFilter.getCapacity(); - - if( l < 0 ) - { - k -= l; - l = 0; - } - - if( r > SrcFilterLen ) - { - r = SrcFilterLen; - } - - const double* const extfltb = extflt + k; - const double* const srcfltb = srcflt + l; - double s = 0.0; - l = r - l; - - for( i = 0; i < l; i++ ) - { - s += extfltb[ i ] * srcfltb[ i ]; - } - - op[ j ] = (fptype) s; - } - } -}; - -/** - * @brief Thread pool for multi-threaded image resizing operation. - * - * This base class is used to organize a multi-threaded image resizing - * operation. The thread pool should consist of threads that initially wait - * for a signal. Upon receiving a signal (via the startAllWorkloads() - * function) each previously added thread should execute its workload's - * process() function once, and return to the wait signal state again. The - * thread pool should be also able to efficiently wait for all workloads to - * finish via the waitAllWorkloadsToFinish() function. - * - * The image resizing algorithm makes calls to functions of this class. - */ - -class CImageResizerThreadPool -{ -public: - CImageResizerThreadPool() - { - } - - virtual ~CImageResizerThreadPool() - { - } - - /** - * @brief Thread pool's workload object class. - * - * This class should be used as a base class for objects that perform the - * actual work spread over several threads. - */ - - class CWorkload - { - public: - virtual ~CWorkload() - { - } - - /** - * Function that gets called from the thread when thread pool's - * startAllWorkloads() function is called. - */ - - virtual void process() = 0; - }; - - /** - * @return The suggested number of workloads (and their associated - * threads) to add. The minimal value this function can return is 1. The - * usual value may depend on the number of physical and virtual cores - * present in the system, and on other considerations. - */ - - virtual int getSuggestedWorkloadCount() const - { - return( 1 ); - } - - /** - * Function adds a new workload (and possibly thread) to the thread pool. - * The caller decides how many parallel workloads (and threads) it - * requires, but this number will not exceed the value returned by the - * getSuggestedWorkloadCount() function. It is implementation-specific how - * many workloads to associate with a single thread. But for efficiency - * reasons each workload should be associated with its own thread. - * - * Note that the same set of workload objects will be processed each time - * the startAllWorkloads() function is called. This means that workload - * objects are added only once. The caller changes the state of the - * workload objects and then calls the startAllWorkloads() function to - * process them. - * - * @param Workload Workload object whose process() function will be called - * from within the thread when the startAllWorkloads() function is called. - */ - - virtual void addWorkload( CWorkload* const Workload ) - { - } - - /** - * Function starts all workloads associated with threads previously added - * via the addWorkload() function. It is assumed that this function - * performs the necessary "memory barrier" (or "cache sync") kind of - * operation so that all threads catch up the prior changes made to the - * workload objects during their wait state. - */ - - virtual void startAllWorkloads() - { - } - - /** - * Function waits for all workloads to finish. - */ - - virtual void waitAllWorkloadsToFinish() - { - } - - /** - * Function removes all workloads previously added via the addWorkload() - * function. This function gets called only after the - * waitAllWorkloadsToFinish() function call. - */ - - virtual void removeAllWorkloads() - { - } -}; - -/** - * @brief Resizing algorithm parameters structure. - * - * This structure holds all selectable parameters used by the resizing - * algorithm at various stages, for both downsizing and upsizing. There are no - * other parameters exist that can optimize the performance of the resizing - * algorithm. Filter length parameters can take fractional values. - * - * Beside quality, these parameters (except Alpha parameters) directly affect - * the computative cost of the resizing algorithm. It is possible to trade - * the visual quality for computative cost. - * - * Anti-alias filtering during downsizing can be defined as a considerable - * reduction of contrast of smallest features of an image. Unfortunately, such - * de-contrasting partially affects features of all sizes thus producing a - * non-linearity of frequency response. All pre-defined parameter sets are - * described by 3 values separated by slashes. The first value is the - * de-contrasting factor of small features (which are being removed) while - * the second value is the de-contrasting factor of large features (which - * should remain intact), with value of 1 equating to "no contrast change". - * The third value is the optimization score (see below), with value of 0 - * equating to the "perfect" linearity of frequency response. - * - * The pre-defined parameter sets offered by this library were auto-optimized - * for the given LPFltBaseLen, IntFltLen and CorrFltAlpha values. The - * optimization goal was to minimize the score: the sum of squares of the - * difference between original and processed images (which was not actually - * resized, k=1). The original image was a 0.5 megapixel uniformly-distributed - * white-noise image with pixel intensities in the 0-1 range. Such goal - * converges very well and produces filtering system with the flattest - * frequency response possible for the given constraints. With this goal, - * increasing the LPFltBaseLen value reduces the general amount of aliasing - * artifacts. - */ - -struct CImageResizerParams -{ - double CorrFltAlpha; ///< Alpha parameter of the Peaked Cosine window - ///< function used on the correction filter. The "usable" values are - ///< in the narrow range 1.0 to 1.5. - ///< - double CorrFltLen; ///< Correction filter's length in samples (taps). The - ///< "usable" range is narrow, 5.5 to 8, as to minimize the - ///< "overcorrection" which is mathematically precise, but visually - ///< unacceptable. - ///< - double IntFltAlpha; ///< Alpha parameter of the Peaked Cosine window - ///< function used on the interpolation low-pass filter. The "usable" - ///< values are in the range 1.5 to 2.5. - ///< - double IntFltCutoff; ///< Interpolation low-pass filter's cutoff frequency - ///< (normalized, [0; 1]). The "usable" range is 0.6 to 0.8. - ///< - double IntFltLen; ///< Interpolation low-pass filter's length in samples - ///< (taps). The length value should be at least 18 or otherwise a - ///< "dark grid" artifact will be introduced if a further sharpening - ///< is applied. IntFltLen together with other IntFlt parameters - ///< should be tuned in a way that produces the flattest frequency - ///< response in 0-0.5 normalized frequency range (this range is due - ///< to 2X upsampling). - ///< - double LPFltAlpha; ///< Alpha parameter of the Peaked Cosine window - ///< function used on the low-pass filter. The "usable" values are - ///< in the range 1.5 to 6.5. - ///< - double LPFltBaseLen; ///< Base length of the low-pass (aka anti-aliasing - ///< or reconstruction) filter, in samples (taps), further adjusted by - ///< the actual cutoff frequency, upsampling and downsampling factors. - ///< The "usable" range is between 6 and 9. - ///< - double LPFltCutoffMult; ///< Low-pass filter's cutoff frequency - ///< multiplier. This value can be both below and above 1.0 as - ///< low-pass filters are inserted on downsampling and upsampling - ///< steps and always have corner frequency equal to or below 0.5pi. - ///< This multiplier shifts low-pass filter's corner frequency towards - ///< lower (if below 1.0) or higher (if above 1.0) frequencies. This - ///< multiplier can be way below 1.0 since any additional - ///< high-frequency damping will be partially corrected by the - ///< correction filter. The "usable" range is 0.3 to 1.0. - ///< - - CImageResizerParams() - : HBFltAlpha( 1.94609 ) - , HBFltCutoff( 0.46437 ) - , HBFltLen( 24 ) - { - } - - double HBFltAlpha; ///< Half-band filter's Alpha. Assigned internally. - ///< - double HBFltCutoff; ///< Half-band filter's cutoff point [0; 1]. Assigned - ///< internally. - ///< - double HBFltLen; ///< Length of the half-band low-pass filter. Assigned - ///< internally. Internally used to perform 2X or higher downsampling. - ///< These filter parameters should be treated as "technical" and do - ///< not require adjustment as they were tuned to suit all - ///< combinations of other parameters. This half-band filter provides - ///< a wide transition band (for minimal ringing artifacts) and a high - ///< stop-band attenuation (for minimal aliasing). - ///< -}; - -/** - * @brief The default set of resizing algorithm parameters - * (10.06/1.88/1.029(256064.90)/0.000039). - * - * This is the default set of resizing parameters that was designed to deliver - * a sharp image while still providing a low amount of ringing artifacts, and - * having a reasonable computational cost. - */ - -struct CImageResizerParamsDef : public CImageResizerParams -{ - CImageResizerParamsDef() - { - CorrFltAlpha = 0.97946;//10.06/1.88/1.029(256064.90)/0.000039:258649,447179 - CorrFltLen = 6.4262; - IntFltAlpha = 6.41341; - IntFltCutoff = 0.7372; - IntFltLen = 18; - LPFltAlpha = 4.76449; - LPFltBaseLen = 7.55999999999998; - LPFltCutoffMult = 0.79285; - } -}; - -/** - * @brief Set of resizing algorithm parameters for ultra-low-ringing - * performance (7.50/2.01/1.083(11568559.86)/0.000001). - * - * This set of resizing algorithm parameters offers the lowest amount of - * ringing this library is capable of providing while still offering a decent - * quality. Low ringing is attained at the expense of higher aliasing - * artifacts and a slightly reduced contrast. - */ - -struct CImageResizerParamsULR : public CImageResizerParams -{ - CImageResizerParamsULR() - { - CorrFltAlpha = 0.95521;//7.50/2.01/1.083(11568559.86)/0.000001:258649,434609 - CorrFltLen = 5.70774; - IntFltAlpha = 1.00766; - IntFltCutoff = 0.74202; - IntFltLen = 18; - LPFltAlpha = 1.6801; - LPFltBaseLen = 6.62; - LPFltCutoffMult = 0.67821; - } -}; - -/** - * @brief Set of resizing algorithm parameters for low-ringing performance - * (7.91/1.96/1.065(1980857.66)/0.000004). - * - * This set of resizing algorithm parameters offers a very low-ringing - * performance at the expense of higher aliasing artifacts and a slightly - * reduced contrast. - */ - -struct CImageResizerParamsLR : public CImageResizerParams -{ - CImageResizerParamsLR() - { - CorrFltAlpha = 1;//7.91/1.96/1.065(1980857.66)/0.000004:258649,437578 - CorrFltLen = 5.865; - IntFltAlpha = 1.79529; - IntFltCutoff = 0.74325; - IntFltLen = 18; - LPFltAlpha = 1.87597; - LPFltBaseLen = 6.89999999999999; - LPFltCutoffMult = 0.69326; - } -}; - -/** - * @brief Set of resizing algorithm parameters for lower-ringing performance - * (9.21/1.91/1.040(391960.71)/0.000023). - * - * This set of resizing algorithm parameters offers a lower-ringing - * performance in comparison to the default setting, at the expense of higher - * aliasing artifacts and a slightly reduced contrast. - */ - -struct CImageResizerParamsLow : public CImageResizerParams -{ - CImageResizerParamsLow() - { - CorrFltAlpha = 0.99739;//9.21/1.91/1.040(391960.71)/0.000023:258649,444105 - CorrFltLen = 6.20326; - IntFltAlpha = 4.6836; - IntFltCutoff = 0.73879; - IntFltLen = 18; - LPFltAlpha = 7.86565; - LPFltBaseLen = 6.91999999999999; - LPFltCutoffMult = 0.78379; - } -}; - -/** - * @brief Set of resizing algorithm parameters for low-aliasing - * resizing (11.59/1.84/1.015(73054.59)/0.000159). - * - * This set of resizing algorithm parameters offers a considerable - * anti-aliasing performance with a good frequency response linearity (and - * contrast). This is an intermediate setting between the default and Ultra - * parameters. - */ - -struct CImageResizerParamsHigh : public CImageResizerParams -{ - CImageResizerParamsHigh() - { - CorrFltAlpha = 0.97433;//11.59/1.84/1.015(73054.59)/0.000159:258649,451830 - CorrFltLen = 6.87893; - IntFltAlpha = 7.74731; - IntFltCutoff = 0.73844; - IntFltLen = 18; - LPFltAlpha = 4.8149; - LPFltBaseLen = 8.07999999999996; - LPFltCutoffMult = 0.79335; - } -}; - -/** - * @brief Set of resizing algorithm parameters for ultra low-aliasing - * resizing (13.68/1.79/1.000(521792.07)/0.000026). - * - * This set of resizing algorithm parameters offers a very considerable - * anti-aliasing performance with a good frequency response linearity (and - * contrast). This set of parameters is computationally expensive and may - * produce ringing artifacts on sharp features. - */ - -struct CImageResizerParamsUltra : public CImageResizerParams -{ - CImageResizerParamsUltra() - { - CorrFltAlpha = 0.99705;//13.68/1.79/1.000(521792.07)/0.000026:258649,457973 - CorrFltLen = 7.42695; - IntFltAlpha = 1.71985; - IntFltCutoff = 0.7571; - IntFltLen = 18; - LPFltAlpha = 6.71313; - LPFltBaseLen = 8.27999999999996; - LPFltCutoffMult = 0.78413; - } -}; - -/** - * @brief Image resizing variables class. - * - * This is an utility "catch all" class that defines various variables used - * during image resizing. Several variables that are explicitly initialized in - * this class' constructor are also used as additional "input" variables to - * the image resizing function. These variables will not be changed by the - * avir::CImageResizer<>::resizeImage() function. - */ - -class CImageResizerVars -{ -public: - int ElCount; ///< The number of "fptype" elements used to store 1 pixel. - ///< - int ElCountIO; ///< The number of source and destination image's elements - ///< used to store 1 pixel. - ///< - int fppack; ///< The number of atomic types stored in a single "fptype" - ///< element. - ///< - int fpalign; ///< Suggested alignment size in bytes. This is not a - ///< required alignment, because image resizing algorithm cannot be - ///< made to have a strictly aligned data access in all cases (e.g. - ///< de-interleaved interpolation cannot perform aligned accesses). - ///< - int elalign; ///< Length alignment of arrays of elements. This applies to - ///< filters and intermediate buffers: this constant forces filters - ///< and scanlines to have a length which is a multiple of this value, - ///< for more efficient SIMD implementation. - ///< - int packmode; ///< 0 if interleaved packing, 1 if de-interleaved. - ///< - int BufLen[ 2 ]; ///< Intermediate buffers' lengths in "fptype" elements. - int BufOffs[ 2 ]; ///< Offsets into the intermediate buffers, used to - ///< provide prefix elements required during processing so that no - ///< "out of range" access happens. This offset is a multiple of - ///< ElCount if pixels are stored in interleaved form. - ///< - double k; ///< Resizing step coefficient, updated to reflect the actually - ///< used coefficient during resizing. - ///< - double o; ///< Starting pixel offset inside the source image, updated to - ///< reflect the actually used offset during resizing. - ///< - int ResizeStep; ///< Index of the resizing step in the latest filtering - ///< steps array. - ///< - bool IsResize2; ///< Use optimized "doResize2" function. - ///< - double InGammaMult; ///< Input gamma multiplier, used to convert input - ///< data to 0 to 1 range. 0.0 if no gamma is in use. - ///< - double OutGammaMult; ///< Output gamma multiplier, used to convert data to - ///< 0 to 255/65535 range. 0.0 if no gamma is in use. - ///< - - double ox; ///< Start X pixel offset within source image (can be - ///< negative). Positive offset moves image to the left. - ///< - double oy; ///< Start Y pixel offset within source image (can be - ///< negative). Positive offset moves image to the top. - ///< - CImageResizerThreadPool* ThreadPool; ///< Thread pool to be used by the - ///< image resizing function. Set to NULL to use single-threaded - ///< processing. - ///< - bool UseSRGBGamma; ///< Perform sRGB gamma linearization (correction). - ///< - int BuildMode; ///< The build mode to use, for debugging purposes. Set to - ///< -1 to select a minimal-complexity mode automatically. All build - ///< modes deliver similar results with minor deviations. - ///< - int RndSeed; ///< Random seed parameter. This parameter may be incremented - ///< after each random generator initialization. The use of this - ///< variable depends on the ditherer implementation. - ///< - - CImageResizerVars() - : ox( 0.0 ) - , oy( 0.0 ) - , ThreadPool( NULL ) - , UseSRGBGamma( false ) - , BuildMode( -1 ) - , RndSeed( 0 ) - { - } -}; - -/** - * @brief Image resizer's filtering step class. - * - * Class defines data to perform a single filtering step over a whole - * horizontal or vertical scanline. Resizing consists of 1 or more steps that - * may be performed before the actual resizing takes place. Filtering may also - * follow a resizing step. Each step must ensure that scanline data contains - * enough pixels to perform the next step (which may be resizing) without - * exceeding scanline's bounds. - * - * A derived class must implement several "const" and "static" functions that - * are used to perform the actual filtering in interleaved or de-interleaved - * mode. - * - * @tparam fptype Floating point type to use for storing pixel elements. SIMD - * types can be used: in this case each element may hold a whole pixel. - * @tparam fptypeatom The atomic type the "fptype" consists of. - */ - -template< class fptype, class fptypeatom > -class CImageResizerFilterStep -{ - AVIR_NOCTOR( CImageResizerFilterStep ); - -public: - bool IsUpsample; ///< "True" if this step is an upsampling step, "false" - ///< if downsampling step. Should be set to "false" if ResampleFactor - ///< equals 0. - ///< - int ResampleFactor; ///< Resample factor (>=1). If 0, this is a resizing - ///< step. This value should be >1 if IsUpsample equals "true". - ///< - CBuffer< fptype > Flt; ///< Filter to use at this step. - ///< - CFltBuffer FltOrig; ///< Originally-designed filter. This buffer may not - ///< be assigned. Assigned by filters that precede the resizing step - ///< if such filter is planned to be embedded into the interpolation - ///< filter as "external" filter. If IsUpsample=true and this filter - ///< buffer is not empty, the upsampling step will not itself apply - ///< any filtering over upsampled input scanline. - ///< - double DCGain; ///< DC gain which was applied to the filter. Not defined - ///< if ResampleFactor = 0. - ///< - int FltLatency; ///< Filter's latency (group delay, shift) in pixels. - ///< - const CImageResizerVars* Vars; ///< Image resizing-related variables. - ///< - int InLen; ///< Input scanline's length in pixels. - ///< - int InBuf; ///< Input buffer index, 0 or 1. - ///< - int InPrefix; ///< Required input prefix pixels. These prefix pixels will - ///< be filled with source scanline's first pixel value. If IsUpsample - ///< is "true", this is the additional number of times the first pixel - ///< will be filtered before processing scanline, this number is also - ///< reflected in the OutPrefix. - ///< - int InSuffix; ///< Required input suffix pixels. These suffix pixels will - ///< be filled with source scanline's last pixel value. If IsUpsample - ///< is "true", this is the additional number of times the last pixel - ///< will be filtered before processing scanline, this number is also - ///< reflected in the OutSuffix. - ///< - int InElIncr; ///< Pixel element increment within the input buffer, used - ///< during de-interleaved processing: in this case each image's - ///< channel is stored independently, InElIncr elements apart. - ///< - int OutLen; ///< Length of the resulting scanline. - ///< - int OutBuf; ///< Output buffer index. 0 or 1; 2 for the last step. - ///< - int OutPrefix; ///< Required output prefix pixels. These prefix pixels - ///< will not be pre-filled with any values. Value is valid only if - ///< IsUpsample equals "true". - ///< - int OutSuffix; ///< Required input suffix pixels. These suffix pixels will - ///< not be pre-filled with any values. Value is valid only if - ///< IsUpsample equals "true". - ///< - int OutElIncr; ///< Pixel element increment within the output buffer, used - ///< during de-interleaved processing. Equals to the InBufElIncr of - ///< the next step. - ///< - CBuffer< fptype > PrefixDC; ///< DC component fluctuations added at the - ///< start of the resulting scanline, used when IsUpsample equals - ///< "true". - ///< - CBuffer< fptype > SuffixDC; ///< DC component fluctuations added at the - ///< end of the resulting scanline, used when IsUpsample equals - ///< "true". - ///< - int EdgePixelCount; ///< The number of edge pixels added. Affects the - ///< initial position within the input scanline, used to produce edge - ///< pixels. This variable is used and should be defined when - ///< IsUpsample=false and ResampleFactor>0. When assigning this - ///< variable it is also necessary to update InPrefix, OutLen and - ///< Vars.o variables. - ///< - static const int EdgePixelCountDef = 3; ///< The default number of pixels - ///< additionally produced at scanline edges during filtering. This is - ///< required to reduce edge artifacts. - ///< - - /** - * @brief Resizing position structure. - * - * Structure holds resizing position and pointer to fractional delay - * filter. - */ - - struct CResizePos - { - int SrcPosInt; ///< Source scanline position. - ///< - int fti; ///< Fractional delay filter index. - ///< - const fptype* ftp; ///< Fractional delay filter pointer. - ///< - fptypeatom x; ///< Interpolation coefficient between delay filters. - ///< - int SrcOffs; ///< Source scanline offset. - ///< - int fl; ///< Filter length to use, applies to doResize2() only. - ///< - }; - - /** - * @brief Resizing positions buffer class. - * - * This class combines buffer together with variables that define resizing - * stepping. - */ - - class CRPosBuf : public CBuffer< CResizePos > - { - public: - double k; ///< Resizing step. - ///< - double o; ///< Resizing offset. - ///< - int FracCount; ///< The number of fractional delay filters in a filter - ///< bank used together with this buffer. - ///< - }; - - /** - * @brief Resizing positions buffer array class. - * - * This class combines structure array of the CRPosBuf class objects with - * the function that locates or creates buffer with the required resizing - * stepping. - */ - - class CRPosBufArray : public CStructArray< CRPosBuf > - { - public: - using CStructArray< CRPosBuf > :: add; - using CStructArray< CRPosBuf > :: getItemCount; - - /** - * Function returns the resizing positions buffer with the required - * stepping. If no such buffer exists, it is created. - * - * @param k Resizing step. - * @param o Resizing offset. - * @param FracCount The number of fractional delay filters in a filter - * bank used together with this buffer. - * @return Reference to the CRPosBuf object. - */ - - CRPosBuf& getRPosBuf( const double k, const double o, - const int FracCount ) - { - int i; - - for( i = 0; i < getItemCount(); i++ ) - { - CRPosBuf& Buf = (*this)[ i ]; - - if( Buf.k == k && Buf.o == o && Buf.FracCount == FracCount ) - { - return( Buf ); - } - } - - CRPosBuf& NewBuf = add(); - NewBuf.k = k; - NewBuf.o = o; - NewBuf.FracCount = FracCount; - - return( NewBuf ); - } - }; - - CRPosBuf* RPosBuf; ///< Resizing positions buffer. Used when - ///< ResampleFactor equals 0 (resizing step). - ///< - CDSPFracFilterBankLin< fptype >* FltBank; ///< Filter bank in use by *this - ///< resizing step. - ///< - - CImageResizerFilterStep() - { - } -}; - -/** - * @brief Interleaved filtering steps implementation class. - * - * This class implements scanline filtering functions in interleaved mode. - * This means that each pixel is processed independently, not in groups. - * - * @tparam fptype Floating point type to use for storing pixel elements. SIMD - * types can be used: in this case each element may hold a whole pixel. - * @tparam fptypeatom The atomic type the "fptype" consists of. - */ - -template< class fptype, class fptypeatom > -class CImageResizerFilterStepINL : - public CImageResizerFilterStep< fptype, fptypeatom > -{ -public: - using CImageResizerFilterStep< fptype, fptypeatom > :: IsUpsample; - using CImageResizerFilterStep< fptype, fptypeatom > :: ResampleFactor; - using CImageResizerFilterStep< fptype, fptypeatom > :: Flt; - using CImageResizerFilterStep< fptype, fptypeatom > :: FltOrig; - using CImageResizerFilterStep< fptype, fptypeatom > :: FltLatency; - using CImageResizerFilterStep< fptype, fptypeatom > :: Vars; - using CImageResizerFilterStep< fptype, fptypeatom > :: InLen; - using CImageResizerFilterStep< fptype, fptypeatom > :: InPrefix; - using CImageResizerFilterStep< fptype, fptypeatom > :: InSuffix; - using CImageResizerFilterStep< fptype, fptypeatom > :: OutLen; - using CImageResizerFilterStep< fptype, fptypeatom > :: OutPrefix; - using CImageResizerFilterStep< fptype, fptypeatom > :: OutSuffix; - using CImageResizerFilterStep< fptype, fptypeatom > :: PrefixDC; - using CImageResizerFilterStep< fptype, fptypeatom > :: SuffixDC; - using CImageResizerFilterStep< fptype, fptypeatom > :: RPosBuf; - using CImageResizerFilterStep< fptype, fptypeatom > :: FltBank; - using CImageResizerFilterStep< fptype, fptypeatom > :: EdgePixelCount; - - /** - * Function performs "packing" of a scanline, and type conversion. - * Scanline, depending on the "fptype" can be potentially stored as a - * packed SIMD values having a certain atomic type. If required, the sRGB - * gamma correction is applied. - * - * @param ip Input scanline. - * @param op0 Output scanline. - * @param l0 The number of pixels to "pack". - */ - - template< class Tin > - void packScanline( const Tin* ip, fptype* const op0, const int l0 ) const - { - const int ElCount = Vars -> ElCount; - const int ElCountIO = Vars -> ElCountIO; - fptype* op = op0; - int l = l0; - - if( !Vars -> UseSRGBGamma ) - { - if( ElCountIO == 1 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) op; - v[ 0 ] = (fptypeatom) ip[ 0 ]; - op += ElCount; - ip++; - l--; - } - } - else - if( ElCountIO == 4 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) op; - v[ 0 ] = (fptypeatom) ip[ 0 ]; - v[ 1 ] = (fptypeatom) ip[ 1 ]; - v[ 2 ] = (fptypeatom) ip[ 2 ]; - v[ 3 ] = (fptypeatom) ip[ 3 ]; - op += ElCount; - ip += 4; - l--; - } - } - else - if( ElCountIO == 3 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) op; - v[ 0 ] = (fptypeatom) ip[ 0 ]; - v[ 1 ] = (fptypeatom) ip[ 1 ]; - v[ 2 ] = (fptypeatom) ip[ 2 ]; - op += ElCount; - ip += 3; - l--; - } - } - else - if( ElCountIO == 2 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) op; - v[ 0 ] = (fptypeatom) ip[ 0 ]; - v[ 1 ] = (fptypeatom) ip[ 1 ]; - op += ElCount; - ip += 2; - l--; - } - } - } - else - { - const fptypeatom gm = (fptypeatom) Vars -> InGammaMult; - - if( ElCountIO == 1 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) op; - v[ 0 ] = convertSRGB2Lin( (fptypeatom) ip[ 0 ] * gm ); - op += ElCount; - ip++; - l--; - } - } - else - if( ElCountIO == 4 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) op; - v[ 0 ] = convertSRGB2Lin( (fptypeatom) ip[ 0 ] * gm ); - v[ 1 ] = convertSRGB2Lin( (fptypeatom) ip[ 1 ] * gm ); - v[ 2 ] = convertSRGB2Lin( (fptypeatom) ip[ 2 ] * gm ); - v[ 3 ] = convertSRGB2Lin( (fptypeatom) ip[ 3 ] * gm ); - op += ElCount; - ip += 4; - l--; - } - } - else - if( ElCountIO == 3 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) op; - v[ 0 ] = convertSRGB2Lin( (fptypeatom) ip[ 0 ] * gm ); - v[ 1 ] = convertSRGB2Lin( (fptypeatom) ip[ 1 ] * gm ); - v[ 2 ] = convertSRGB2Lin( (fptypeatom) ip[ 2 ] * gm ); - op += ElCount; - ip += 3; - l--; - } - } - else - if( ElCountIO == 2 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) op; - v[ 0 ] = convertSRGB2Lin( (fptypeatom) ip[ 0 ] * gm ); - v[ 1 ] = convertSRGB2Lin( (fptypeatom) ip[ 1 ] * gm ); - op += ElCount; - ip += 2; - l--; - } - } - } - - const int ZeroCount = ElCount * Vars -> fppack - ElCountIO; - op = (fptype*) ( (fptypeatom*) op0 + ElCountIO ); - l = l0; - - if( ZeroCount == 1 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) op; - v[ 0 ] = (fptypeatom) 0; - op += ElCount; - l--; - } - } - else - if( ZeroCount == 2 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) op; - v[ 0 ] = (fptypeatom) 0; - v[ 1 ] = (fptypeatom) 0; - op += ElCount; - l--; - } - } - else - if( ZeroCount == 3 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) op; - v[ 0 ] = (fptypeatom) 0; - v[ 1 ] = (fptypeatom) 0; - v[ 2 ] = (fptypeatom) 0; - op += ElCount; - l--; - } - } - } - - /** - * Function applies Linear to sRGB gamma correction to the specified - * scanline. - * - * @param p Scanline. - * @param l The number of pixels to de-linearize. - * @param Vars0 Image resizing-related variables. - */ - - static void applySRGBGamma( fptype* p, int l, - const CImageResizerVars& Vars0 ) - { - const int ElCount = Vars0.ElCount; - const int ElCountIO = Vars0.ElCountIO; - const fptypeatom gm = (fptypeatom) Vars0.OutGammaMult; - - if( ElCountIO == 1 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) p; - v[ 0 ] = convertLin2SRGB( v[ 0 ]) * gm; - p += ElCount; - l--; - } - } - else - if( ElCountIO == 4 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) p; - v[ 0 ] = convertLin2SRGB( v[ 0 ]) * gm; - v[ 1 ] = convertLin2SRGB( v[ 1 ]) * gm; - v[ 2 ] = convertLin2SRGB( v[ 2 ]) * gm; - v[ 3 ] = convertLin2SRGB( v[ 3 ]) * gm; - p += ElCount; - l--; - } - } - else - if( ElCountIO == 3 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) p; - v[ 0 ] = convertLin2SRGB( v[ 0 ]) * gm; - v[ 1 ] = convertLin2SRGB( v[ 1 ]) * gm; - v[ 2 ] = convertLin2SRGB( v[ 2 ]) * gm; - p += ElCount; - l--; - } - } - else - if( ElCountIO == 2 ) - { - while( l > 0 ) - { - fptypeatom* v = (fptypeatom*) p; - v[ 0 ] = convertLin2SRGB( v[ 0 ]) * gm; - v[ 1 ] = convertLin2SRGB( v[ 1 ]) * gm; - p += ElCount; - l--; - } - } - } - - /** - * Function converts vertical scanline to horizontal scanline. This - * function is called by the image resizer when image is resized - * vertically. This means that the vertical scanline is stored in the - * same format produced by the packScanline() and maintained by other - * filtering functions. - * - * @param ip Input vertical scanline. - * @param op Output buffer (temporary buffer used during resizing). - * @param SrcLen The number of pixels in the input scanline, also used to - * calculate input buffer increment. - * @param SrcIncr Input buffer increment to the next vertical pixel. - */ - - void convertVtoH( const fptype* ip, fptype* op, const int SrcLen, - const int SrcIncr ) const - { - const int ElCount = Vars -> ElCount; - int j; - - if( ElCount == 1 ) - { - for( j = 0; j < SrcLen; j++ ) - { - op[ 0 ] = ip[ 0 ]; - ip += SrcIncr; - op++; - } - } - else - if( ElCount == 4 ) - { - for( j = 0; j < SrcLen; j++ ) - { - op[ 0 ] = ip[ 0 ]; - op[ 1 ] = ip[ 1 ]; - op[ 2 ] = ip[ 2 ]; - op[ 3 ] = ip[ 3 ]; - ip += SrcIncr; - op += 4; - } - } - else - if( ElCount == 3 ) - { - for( j = 0; j < SrcLen; j++ ) - { - op[ 0 ] = ip[ 0 ]; - op[ 1 ] = ip[ 1 ]; - op[ 2 ] = ip[ 2 ]; - ip += SrcIncr; - op += 3; - } - } - else - if( ElCount == 2 ) - { - for( j = 0; j < SrcLen; j++ ) - { - op[ 0 ] = ip[ 0 ]; - op[ 1 ] = ip[ 1 ]; - ip += SrcIncr; - op += 2; - } - } - } - - /** - * Function performs "unpacking" of a scanline and type conversion - * (truncation is used when floating point is converted to integer). - * Scanline, depending on the "fptype" can be potentially stored as a - * packed SIMD values having a certain atomic type. The unpacking function - * assumes that scanline is stored in the style produced by the - * packScanline() function. - * - * @param ip Input scanline. - * @param op Output scanline. - * @param l The number of pixels to "unpack". - * @param Vars0 Image resizing-related variables. - */ - - template< class Tout > - static void unpackScanline( const fptype* ip, Tout* op, int l, - const CImageResizerVars& Vars0 ) - { - const int ElCount = Vars0.ElCount; - const int ElCountIO = Vars0.ElCountIO; - - if( ElCountIO == 1 ) - { - while( l > 0 ) - { - const fptypeatom* v = (const fptypeatom*) ip; - op[ 0 ] = (Tout) v[ 0 ]; - ip += ElCount; - op++; - l--; - } - } - else - if( ElCountIO == 4 ) - { - while( l > 0 ) - { - const fptypeatom* v = (const fptypeatom*) ip; - op[ 0 ] = (Tout) v[ 0 ]; - op[ 1 ] = (Tout) v[ 1 ]; - op[ 2 ] = (Tout) v[ 2 ]; - op[ 3 ] = (Tout) v[ 3 ]; - ip += ElCount; - op += 4; - l--; - } - } - else - if( ElCountIO == 3 ) - { - while( l > 0 ) - { - const fptypeatom* v = (const fptypeatom*) ip; - op[ 0 ] = (Tout) v[ 0 ]; - op[ 1 ] = (Tout) v[ 1 ]; - op[ 2 ] = (Tout) v[ 2 ]; - ip += ElCount; - op += 3; - l--; - } - } - else - if( ElCountIO == 2 ) - { - while( l > 0 ) - { - const fptypeatom* v = (const fptypeatom*) ip; - op[ 0 ] = (Tout) v[ 0 ]; - op[ 1 ] = (Tout) v[ 1 ]; - ip += ElCount; - op += 2; - l--; - } - } - } - - /** - * Function calculates scanline's DC gain for each channel, further used - * to "unbias" the scanline. - * - * @param p Source scanline. - * @param SrcLen Source scanline's length. - * @param[out] ElBiases Resuling biases. - */ - - void calcScanlineBias( const fptype* p, const int SrcLen, - fptype* const ElBiases ) const - { - const int ElCount = Vars -> ElCount; - int l = SrcLen; - - if( ElCount == 1 ) - { - fptype b0 = (fptype) 0; - - while( l > 0 ) - { - b0 += p[ 0 ]; - p++; - l--; - } - - ElBiases[ 0 ] = b0 / (fptype) SrcLen; - } - else - if( ElCount == 4 ) - { - fptype b0 = (fptype) 0; - fptype b1 = (fptype) 0; - fptype b2 = (fptype) 0; - fptype b3 = (fptype) 0; - - while( l > 0 ) - { - b0 += p[ 0 ]; - b1 += p[ 1 ]; - b2 += p[ 2 ]; - b3 += p[ 3 ]; - p += 4; - l--; - } - - ElBiases[ 0 ] = b0 / (fptype) SrcLen; - ElBiases[ 1 ] = b1 / (fptype) SrcLen; - ElBiases[ 2 ] = b2 / (fptype) SrcLen; - ElBiases[ 3 ] = b3 / (fptype) SrcLen; - } - else - if( ElCount == 3 ) - { - fptype b0 = (fptype) 0; - fptype b1 = (fptype) 0; - fptype b2 = (fptype) 0; - - while( l > 0 ) - { - b0 += p[ 0 ]; - b1 += p[ 1 ]; - b2 += p[ 2 ]; - p += 3; - l--; - } - - ElBiases[ 0 ] = b0 / (fptype) SrcLen; - ElBiases[ 1 ] = b1 / (fptype) SrcLen; - ElBiases[ 2 ] = b2 / (fptype) SrcLen; - } - else - if( ElCount == 2 ) - { - fptype b0 = (fptype) 0; - fptype b1 = (fptype) 0; - - while( l > 0 ) - { - b0 += p[ 0 ]; - b1 += p[ 1 ]; - p += 2; - l--; - } - - ElBiases[ 0 ] = b0 / (fptype) SrcLen; - ElBiases[ 1 ] = b1 / (fptype) SrcLen; - } - } - - /** - * Function applies "unbiasing" to the scanline, by subtracting the - * previously calculated bias (DC gain) values. - * - * @param p Scanline. - * @param l Scanline's length. - * @param ElBiases Biases to subtract, for each channel. - */ - - void unbiasScanline( fptype* p, int l, - const fptype* const ElBiases ) const - { - const int ElCount = Vars -> ElCount; - - if( ElCount == 1 ) - { - const fptype b0 = ElBiases[ 0 ]; - - while( l > 0 ) - { - p[ 0 ] -= b0; - p++; - l--; - } - } - else - if( ElCount == 4 ) - { - const fptype b0 = ElBiases[ 0 ]; - const fptype b1 = ElBiases[ 1 ]; - const fptype b2 = ElBiases[ 2 ]; - const fptype b3 = ElBiases[ 3 ]; - - while( l > 0 ) - { - p[ 0 ] -= b0; - p[ 1 ] -= b1; - p[ 2 ] -= b2; - p[ 3 ] -= b3; - p += 4; - l--; - } - } - else - if( ElCount == 3 ) - { - const fptype b0 = ElBiases[ 0 ]; - const fptype b1 = ElBiases[ 1 ]; - const fptype b2 = ElBiases[ 2 ]; - - while( l > 0 ) - { - p[ 0 ] -= b0; - p[ 1 ] -= b1; - p[ 2 ] -= b2; - p += 3; - l--; - } - } - else - if( ElCount == 2 ) - { - const fptype b0 = ElBiases[ 0 ]; - const fptype b1 = ElBiases[ 1 ]; - - while( l > 0 ) - { - p[ 0 ] -= b0; - p[ 1 ] -= b1; - p += 2; - l--; - } - } - } - - /** - * Function prepares input scanline buffer for *this filtering step. - * Left- and right-most pixels are replicated to make sure no buffer - * overrun happens. Such approach also allows to bypass any pointer - * range checks. - * - * @param Src Source buffer. - */ - - void prepareInBuf( fptype* Src ) const - { - if( IsUpsample || InPrefix + InSuffix == 0 ) - { - return; - } - - const int ElCount = Vars -> ElCount; - replicateArray( Src, ElCount, Src - ElCount, InPrefix, -ElCount ); - - Src += ( InLen - 1 ) * ElCount; - replicateArray( Src, ElCount, Src + ElCount, InSuffix, ElCount ); - } - - /** - * Function peforms scanline upsampling with filtering. - * - * @param Src Source scanline buffer (length = this -> InLen). Source - * scanline increment will be equal to ElCount. - * @param Dst Destination scanline buffer. - */ - - void doUpsample( const fptype* const Src, fptype* const Dst ) const - { - const int ElCount = Vars -> ElCount; - fptype* op0 = &Dst[ -OutPrefix * ElCount ]; - memset( op0, 0, ( OutPrefix + OutLen + OutSuffix ) * ElCount * - sizeof( fptype )); - - const fptype* ip = Src; - const int opstep = ElCount * ResampleFactor; - int l; - - if( FltOrig.getCapacity() > 0 ) - { - // Do not perform filtering, only upsample. - - op0 += ( OutPrefix % ResampleFactor ) * ElCount; - l = OutPrefix / ResampleFactor; - - if( ElCount == 1 ) - { - while( l > 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0 += opstep; - l--; - } - - l = InLen - 1; - - while( l > 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0 += opstep; - ip += ElCount; - l--; - } - - l = OutSuffix / ResampleFactor; - - while( l >= 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0 += opstep; - l--; - } - } - else - if( ElCount == 4 ) - { - while( l > 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0[ 1 ] = ip[ 1 ]; - op0[ 2 ] = ip[ 2 ]; - op0[ 3 ] = ip[ 3 ]; - op0 += opstep; - l--; - } - - l = InLen - 1; - - while( l > 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0[ 1 ] = ip[ 1 ]; - op0[ 2 ] = ip[ 2 ]; - op0[ 3 ] = ip[ 3 ]; - op0 += opstep; - ip += ElCount; - l--; - } - - l = OutSuffix / ResampleFactor; - - while( l >= 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0[ 1 ] = ip[ 1 ]; - op0[ 2 ] = ip[ 2 ]; - op0[ 3 ] = ip[ 3 ]; - op0 += opstep; - l--; - } - } - else - if( ElCount == 3 ) - { - while( l > 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0[ 1 ] = ip[ 1 ]; - op0[ 2 ] = ip[ 2 ]; - op0 += opstep; - l--; - } - - l = InLen - 1; - - while( l > 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0[ 1 ] = ip[ 1 ]; - op0[ 2 ] = ip[ 2 ]; - op0 += opstep; - ip += ElCount; - l--; - } - - l = OutSuffix / ResampleFactor; - - while( l >= 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0[ 1 ] = ip[ 1 ]; - op0[ 2 ] = ip[ 2 ]; - op0 += opstep; - l--; - } - } - else - if( ElCount == 2 ) - { - while( l > 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0[ 1 ] = ip[ 1 ]; - op0 += opstep; - l--; - } - - l = InLen - 1; - - while( l > 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0[ 1 ] = ip[ 1 ]; - op0 += opstep; - ip += ElCount; - l--; - } - - l = OutSuffix / ResampleFactor; - - while( l >= 0 ) - { - op0[ 0 ] = ip[ 0 ]; - op0[ 1 ] = ip[ 1 ]; - op0 += opstep; - l--; - } - } - - return; - } - - const fptype* const f = Flt; - const int flen = Flt.getCapacity(); - fptype* op; - int i; - - if( ElCount == 1 ) - { - l = InPrefix; - - while( l > 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ i ] += f[ i ] * ip[ 0 ]; - } - - op0 += opstep; - l--; - } - - l = InLen - 1; - - while( l > 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ i ] += f[ i ] * ip[ 0 ]; - } - - ip += ElCount; - op0 += opstep; - l--; - } - - l = InSuffix; - - while( l >= 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ i ] += f[ i ] * ip[ 0 ]; - } - - op0 += opstep; - l--; - } - } - else - if( ElCount == 4 ) - { - l = InPrefix; - - while( l > 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ 0 ] += f[ i ] * ip[ 0 ]; - op[ 1 ] += f[ i ] * ip[ 1 ]; - op[ 2 ] += f[ i ] * ip[ 2 ]; - op[ 3 ] += f[ i ] * ip[ 3 ]; - op += 4; - } - - op0 += opstep; - l--; - } - - l = InLen - 1; - - while( l > 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ 0 ] += f[ i ] * ip[ 0 ]; - op[ 1 ] += f[ i ] * ip[ 1 ]; - op[ 2 ] += f[ i ] * ip[ 2 ]; - op[ 3 ] += f[ i ] * ip[ 3 ]; - op += 4; - } - - ip += ElCount; - op0 += opstep; - l--; - } - - l = InSuffix; - - while( l >= 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ 0 ] += f[ i ] * ip[ 0 ]; - op[ 1 ] += f[ i ] * ip[ 1 ]; - op[ 2 ] += f[ i ] * ip[ 2 ]; - op[ 3 ] += f[ i ] * ip[ 3 ]; - op += 4; - } - - op0 += opstep; - l--; - } - } - else - if( ElCount == 3 ) - { - l = InPrefix; - - while( l > 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ 0 ] += f[ i ] * ip[ 0 ]; - op[ 1 ] += f[ i ] * ip[ 1 ]; - op[ 2 ] += f[ i ] * ip[ 2 ]; - op += 3; - } - - op0 += opstep; - l--; - } - - l = InLen - 1; - - while( l > 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ 0 ] += f[ i ] * ip[ 0 ]; - op[ 1 ] += f[ i ] * ip[ 1 ]; - op[ 2 ] += f[ i ] * ip[ 2 ]; - op += 3; - } - - ip += ElCount; - op0 += opstep; - l--; - } - - l = InSuffix; - - while( l >= 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ 0 ] += f[ i ] * ip[ 0 ]; - op[ 1 ] += f[ i ] * ip[ 1 ]; - op[ 2 ] += f[ i ] * ip[ 2 ]; - op += 3; - } - - op0 += opstep; - l--; - } - } - else - if( ElCount == 2 ) - { - l = InPrefix; - - while( l > 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ 0 ] += f[ i ] * ip[ 0 ]; - op[ 1 ] += f[ i ] * ip[ 1 ]; - op += 2; - } - - op0 += opstep; - l--; - } - - l = InLen - 1; - - while( l > 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ 0 ] += f[ i ] * ip[ 0 ]; - op[ 1 ] += f[ i ] * ip[ 1 ]; - op += 2; - } - - ip += ElCount; - op0 += opstep; - l--; - } - - l = InSuffix; - - while( l >= 0 ) - { - op = op0; - - for( i = 0; i < flen; i++ ) - { - op[ 0 ] += f[ i ] * ip[ 0 ]; - op[ 1 ] += f[ i ] * ip[ 1 ]; - op += 2; - } - - op0 += opstep; - l--; - } - } - - op = op0; - const fptype* dc = SuffixDC; - l = SuffixDC.getCapacity(); - - if( ElCount == 1 ) - { - for( i = 0; i < l; i++ ) - { - op[ i ] += ip[ 0 ] * dc[ i ]; - } - } - else - if( ElCount == 4 ) - { - while( l > 0 ) - { - op[ 0 ] += ip[ 0 ] * dc[ 0 ]; - op[ 1 ] += ip[ 1 ] * dc[ 0 ]; - op[ 2 ] += ip[ 2 ] * dc[ 0 ]; - op[ 3 ] += ip[ 3 ] * dc[ 0 ]; - dc++; - op += 4; - l--; - } - } - else - if( ElCount == 3 ) - { - while( l > 0 ) - { - op[ 0 ] += ip[ 0 ] * dc[ 0 ]; - op[ 1 ] += ip[ 1 ] * dc[ 0 ]; - op[ 2 ] += ip[ 2 ] * dc[ 0 ]; - dc++; - op += 3; - l--; - } - } - else - if( ElCount == 2 ) - { - while( l > 0 ) - { - op[ 0 ] += ip[ 0 ] * dc[ 0 ]; - op[ 1 ] += ip[ 1 ] * dc[ 0 ]; - dc++; - op += 2; - l--; - } - } - - ip = Src; - op = Dst - InPrefix * opstep; - dc = PrefixDC; - l = PrefixDC.getCapacity(); - - if( ElCount == 1 ) - { - for( i = 0; i < l; i++ ) - { - op[ i ] += ip[ 0 ] * dc[ i ]; - } - } - else - if( ElCount == 4 ) - { - while( l > 0 ) - { - op[ 0 ] += ip[ 0 ] * dc[ 0 ]; - op[ 1 ] += ip[ 1 ] * dc[ 0 ]; - op[ 2 ] += ip[ 2 ] * dc[ 0 ]; - op[ 3 ] += ip[ 3 ] * dc[ 0 ]; - dc++; - op += 4; - l--; - } - } - else - if( ElCount == 3 ) - { - while( l > 0 ) - { - op[ 0 ] += ip[ 0 ] * dc[ 0 ]; - op[ 1 ] += ip[ 1 ] * dc[ 0 ]; - op[ 2 ] += ip[ 2 ] * dc[ 0 ]; - dc++; - op += 3; - l--; - } - } - else - if( ElCount == 2 ) - { - while( l > 0 ) - { - op[ 0 ] += ip[ 0 ] * dc[ 0 ]; - op[ 1 ] += ip[ 1 ] * dc[ 0 ]; - dc++; - op += 2; - l--; - } - } - } - - /** - * Function peforms scanline filtering with optional downsampling. - * Function makes use of the symmetry of the filter. - * - * @param Src Source scanline buffer (length = this -> InLen). Source - * scanline increment will be equal to ElCount. - * @param Dst Destination scanline buffer. - * @param DstIncr Destination scanline buffer increment, used for - * horizontal or vertical scanline stepping. - */ - - void doFilter( const fptype* const Src, fptype* Dst, - const int DstIncr ) const - { - const int ElCount = Vars -> ElCount; - const fptype* const f = &Flt[ FltLatency ]; - const int flen = FltLatency + 1; - const int ipstep = ElCount * ResampleFactor; - const fptype* ip = Src - EdgePixelCount * ipstep; - const fptype* ip1; - const fptype* ip2; - int l = OutLen; - int i; - - if( ElCount == 1 ) - { - while( l > 0 ) - { - fptype s = f[ 0 ] * ip[ 0 ]; - ip1 = ip; - ip2 = ip; - - for( i = 1; i < flen; i++ ) - { - ip1++; - ip2--; - s += f[ i ] * ( ip1[ 0 ] + ip2[ 0 ]); - } - - Dst[ 0 ] = s; - Dst += DstIncr; - ip += ipstep; - l--; - } - } - else - if( ElCount == 4 ) - { - while( l > 0 ) - { - fptype s1 = f[ 0 ] * ip[ 0 ]; - fptype s2 = f[ 0 ] * ip[ 1 ]; - fptype s3 = f[ 0 ] * ip[ 2 ]; - fptype s4 = f[ 0 ] * ip[ 3 ]; - ip1 = ip; - ip2 = ip; - - for( i = 1; i < flen; i++ ) - { - ip1 += 4; - ip2 -= 4; - s1 += f[ i ] * ( ip1[ 0 ] + ip2[ 0 ]); - s2 += f[ i ] * ( ip1[ 1 ] + ip2[ 1 ]); - s3 += f[ i ] * ( ip1[ 2 ] + ip2[ 2 ]); - s4 += f[ i ] * ( ip1[ 3 ] + ip2[ 3 ]); - } - - Dst[ 0 ] = s1; - Dst[ 1 ] = s2; - Dst[ 2 ] = s3; - Dst[ 3 ] = s4; - Dst += DstIncr; - ip += ipstep; - l--; - } - } - else - if( ElCount == 3 ) - { - while( l > 0 ) - { - fptype s1 = f[ 0 ] * ip[ 0 ]; - fptype s2 = f[ 0 ] * ip[ 1 ]; - fptype s3 = f[ 0 ] * ip[ 2 ]; - ip1 = ip; - ip2 = ip; - - for( i = 1; i < flen; i++ ) - { - ip1 += 3; - ip2 -= 3; - s1 += f[ i ] * ( ip1[ 0 ] + ip2[ 0 ]); - s2 += f[ i ] * ( ip1[ 1 ] + ip2[ 1 ]); - s3 += f[ i ] * ( ip1[ 2 ] + ip2[ 2 ]); - } - - Dst[ 0 ] = s1; - Dst[ 1 ] = s2; - Dst[ 2 ] = s3; - Dst += DstIncr; - ip += ipstep; - l--; - } - } - else - if( ElCount == 2 ) - { - while( l > 0 ) - { - fptype s1 = f[ 0 ] * ip[ 0 ]; - fptype s2 = f[ 0 ] * ip[ 1 ]; - ip1 = ip; - ip2 = ip; - - for( i = 1; i < flen; i++ ) - { - ip1 += 2; - ip2 -= 2; - s1 += f[ i ] * ( ip1[ 0 ] + ip2[ 0 ]); - s2 += f[ i ] * ( ip1[ 1 ] + ip2[ 1 ]); - } - - Dst[ 0 ] = s1; - Dst[ 1 ] = s2; - Dst += DstIncr; - ip += ipstep; - l--; - } - } - } - - /** - * Function performs resizing of a single scanline. This function does - * not "know" about the length of the source scanline buffer. This buffer - * should be padded with enough pixels so that ( SrcPos - FilterLenD2 ) is - * always >= 0 and ( SrcPos + ( DstLineLen - 1 ) * k + FilterLenD2 + 1 ) - * does not exceed source scanline's buffer length. SrcLine's increment is - * assumed to be equal to ElCount. - * - * @param SrcLine Source scanline buffer. - * @param DstLine Destination (resized) scanline buffer. - * @param DstLineIncr Destination scanline position increment, used for - * horizontal or vertical scanline stepping. - * @param ElBiases Bias values to add to the resulting scanline. - * @param xx Temporary buffer, of size FltBank -> getFilterLen(), must be - * aligned by fpclass :: fpalign. - */ - - void doResize( const fptype* SrcLine, fptype* DstLine, - const int DstLineIncr, const fptype* const ElBiases, - fptype* const ) const - { - const int IntFltLen = FltBank -> getFilterLen(); - const int ElCount = Vars -> ElCount; - const typename CImageResizerFilterStep< fptype, fptypeatom > :: - CResizePos* rpos = &(*RPosBuf)[ 0 ]; - - const typename CImageResizerFilterStep< fptype, fptypeatom > :: - CResizePos* const rpose = rpos + OutLen; - -#define AVIR_RESIZE_PART1 \ - while( rpos < rpose ) \ - { \ - const fptype x = (fptype) rpos -> x; \ - const fptype* const ftp = rpos -> ftp; \ - const fptype* const ftp2 = ftp + IntFltLen; \ - const fptype* Src = SrcLine + rpos -> SrcOffs; \ - int i; - -#define AVIR_RESIZE_PART1nx \ - while( rpos < rpose ) \ - { \ - const fptype* const ftp = rpos -> ftp; \ - const fptype* Src = SrcLine + rpos -> SrcOffs; \ - int i; - -#define AVIR_RESIZE_PART2 \ - DstLine += DstLineIncr; \ - rpos++; \ - } - - if( FltBank -> getOrder() == 1 ) - { - if( ElCount == 1 ) - { - AVIR_RESIZE_PART1 - - fptype sum0 = ElBiases[ 0 ]; - - for( i = 0; i < IntFltLen; i++ ) - { - sum0 += ( ftp[ i ] + ftp2[ i ] * x ) * Src[ i ]; - } - - DstLine[ 0 ] = sum0; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 4 ) - { - AVIR_RESIZE_PART1 - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - fptype sum2 = ElBiases[ 2 ]; - fptype sum3 = ElBiases[ 3 ]; - - for( i = 0; i < IntFltLen; i++ ) - { - const fptype xx = ftp[ i ] + ftp2[ i ] * x; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - sum2 += xx * Src[ 2 ]; - sum3 += xx * Src[ 3 ]; - Src += 4; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - DstLine[ 2 ] = sum2; - DstLine[ 3 ] = sum3; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 3 ) - { - AVIR_RESIZE_PART1 - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - fptype sum2 = ElBiases[ 2 ]; - - for( i = 0; i < IntFltLen; i++ ) - { - const fptype xx = ftp[ i ] + ftp2[ i ] * x; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - sum2 += xx * Src[ 2 ]; - Src += 3; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - DstLine[ 2 ] = sum2; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 2 ) - { - AVIR_RESIZE_PART1 - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - - for( i = 0; i < IntFltLen; i++ ) - { - const fptype xx = ftp[ i ] + ftp2[ i ] * x; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - Src += 2; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - - AVIR_RESIZE_PART2 - } - } - else - { - if( ElCount == 1 ) - { - AVIR_RESIZE_PART1nx - - fptype sum0 = ElBiases[ 0 ]; - - for( i = 0; i < IntFltLen; i++ ) - { - sum0 += ftp[ i ] * Src[ i ]; - } - - DstLine[ 0 ] = sum0; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 4 ) - { - AVIR_RESIZE_PART1nx - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - fptype sum2 = ElBiases[ 2 ]; - fptype sum3 = ElBiases[ 3 ]; - - for( i = 0; i < IntFltLen; i++ ) - { - const fptype xx = ftp[ i ]; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - sum2 += xx * Src[ 2 ]; - sum3 += xx * Src[ 3 ]; - Src += 4; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - DstLine[ 2 ] = sum2; - DstLine[ 3 ] = sum3; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 3 ) - { - AVIR_RESIZE_PART1nx - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - fptype sum2 = ElBiases[ 2 ]; - - for( i = 0; i < IntFltLen; i++ ) - { - const fptype xx = ftp[ i ]; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - sum2 += xx * Src[ 2 ]; - Src += 3; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - DstLine[ 2 ] = sum2; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 2 ) - { - AVIR_RESIZE_PART1nx - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - - for( i = 0; i < IntFltLen; i++ ) - { - const fptype xx = ftp[ i ]; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - Src += 2; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - - AVIR_RESIZE_PART2 - } - } - } -#undef AVIR_RESIZE_PART2 -#undef AVIR_RESIZE_PART1nx -#undef AVIR_RESIZE_PART1 - - /** - * Function performs resizing of a single scanline assuming that the input - * buffer consists of zero-padded elements (2X upsampling without - * filtering). Similar to the doResize() function otherwise. - * - * @param SrcLine Source scanline buffer. - * @param DstLine Destination (resized) scanline buffer. - * @param DstLineIncr Destination scanline position increment, used for - * horizontal or vertical scanline stepping. - * @param ElBiases Bias values to add to the resulting scanline. - * @param xx Temporary buffer, of size FltBank -> getFilterLen(), must be - * aligned by fpclass :: fpalign. - */ - - void doResize2( const fptype* SrcLine, fptype* DstLine, - const int DstLineIncr, const fptype* const ElBiases, - fptype* const ) const - { - const int IntFltLen0 = FltBank -> getFilterLen(); - const int ElCount = Vars -> ElCount; - const typename CImageResizerFilterStep< fptype, fptypeatom > :: - CResizePos* rpos = &(*RPosBuf)[ 0 ]; - - const typename CImageResizerFilterStep< fptype, fptypeatom > :: - CResizePos* const rpose = rpos + OutLen; - -#define AVIR_RESIZE_PART1 \ - while( rpos < rpose ) \ - { \ - const fptype x = (fptype) rpos -> x; \ - const fptype* const ftp = rpos -> ftp; \ - const fptype* const ftp2 = ftp + IntFltLen0; \ - const fptype* Src = SrcLine + rpos -> SrcOffs; \ - const int IntFltLen = rpos -> fl; \ - int i; - -#define AVIR_RESIZE_PART1nx \ - while( rpos < rpose ) \ - { \ - const fptype* const ftp = rpos -> ftp; \ - const fptype* Src = SrcLine + rpos -> SrcOffs; \ - const int IntFltLen = rpos -> fl; \ - int i; - -#define AVIR_RESIZE_PART2 \ - DstLine += DstLineIncr; \ - rpos++; \ - } - - if( FltBank -> getOrder() == 1 ) - { - if( ElCount == 1 ) - { - AVIR_RESIZE_PART1 - - fptype sum0 = ElBiases[ 0 ]; - - for( i = 0; i < IntFltLen; i += 2 ) - { - sum0 += ( ftp[ i ] + ftp2[ i ] * x ) * Src[ i ]; - } - - DstLine[ 0 ] = sum0; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 4 ) - { - AVIR_RESIZE_PART1 - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - fptype sum2 = ElBiases[ 2 ]; - fptype sum3 = ElBiases[ 3 ]; - - for( i = 0; i < IntFltLen; i += 2 ) - { - const fptype xx = ftp[ i ] + ftp2[ i ] * x; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - sum2 += xx * Src[ 2 ]; - sum3 += xx * Src[ 3 ]; - Src += 4 * 2; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - DstLine[ 2 ] = sum2; - DstLine[ 3 ] = sum3; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 3 ) - { - AVIR_RESIZE_PART1 - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - fptype sum2 = ElBiases[ 2 ]; - - for( i = 0; i < IntFltLen; i += 2 ) - { - const fptype xx = ftp[ i ] + ftp2[ i ] * x; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - sum2 += xx * Src[ 2 ]; - Src += 3 * 2; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - DstLine[ 2 ] = sum2; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 2 ) - { - AVIR_RESIZE_PART1 - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - - for( i = 0; i < IntFltLen; i += 2 ) - { - const fptype xx = ftp[ i ] + ftp2[ i ] * x; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - Src += 2 * 2; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - - AVIR_RESIZE_PART2 - } - } - else - { - if( ElCount == 1 ) - { - AVIR_RESIZE_PART1nx - - fptype sum0 = ElBiases[ 0 ]; - - for( i = 0; i < IntFltLen; i += 2 ) - { - sum0 += ftp[ i ] * Src[ i ]; - } - - DstLine[ 0 ] = sum0; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 4 ) - { - AVIR_RESIZE_PART1nx - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - fptype sum2 = ElBiases[ 2 ]; - fptype sum3 = ElBiases[ 3 ]; - - for( i = 0; i < IntFltLen; i += 2 ) - { - const fptype xx = ftp[ i ]; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - sum2 += xx * Src[ 2 ]; - sum3 += xx * Src[ 3 ]; - Src += 4 * 2; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - DstLine[ 2 ] = sum2; - DstLine[ 3 ] = sum3; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 3 ) - { - AVIR_RESIZE_PART1nx - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - fptype sum2 = ElBiases[ 2 ]; - - for( i = 0; i < IntFltLen; i += 2 ) - { - const fptype xx = ftp[ i ]; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - sum2 += xx * Src[ 2 ]; - Src += 3 * 2; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - DstLine[ 2 ] = sum2; - - AVIR_RESIZE_PART2 - } - else - if( ElCount == 2 ) - { - AVIR_RESIZE_PART1nx - - fptype sum0 = ElBiases[ 0 ]; - fptype sum1 = ElBiases[ 1 ]; - - for( i = 0; i < IntFltLen; i += 2 ) - { - const fptype xx = ftp[ i ]; - sum0 += xx * Src[ 0 ]; - sum1 += xx * Src[ 1 ]; - Src += 2 * 2; - } - - DstLine[ 0 ] = sum0; - DstLine[ 1 ] = sum1; - - AVIR_RESIZE_PART2 - } - } - } -#undef AVIR_RESIZE_PART2 -#undef AVIR_RESIZE_PART1nx -#undef AVIR_RESIZE_PART1 -}; - -/** - * @brief Image resizer's default dithering class. - * - * This class defines an object that performs rounding, clipping and dithering - * operations over horizontal scanline pixels before scanline is stored in the - * output buffer. - * - * The ditherer should expect the same storage order of the pixels in a - * scanline as used in the "filtering step" class. So, a separate ditherer - * class should be defined for each scanline pixel storage style. The default - * ditherer implements a simple rounding without dithering: it can be used for - * an efficient dithering method which can be multi-threaded. - * - * @tparam fptype Floating point type to use for storing pixel data. SIMD - * types can be used. - */ - -template< class fptype > -class CImageResizerDithererDefINL -{ -public: - /** - * Function initializes the ditherer object. - * - * @param aLen Scanline length in pixels to process. - * @param aVars Image resizing-related variables. - * @param aTrMul Bit-depth truncation multiplier. 1 - no additional - * truncation. - * @param aPkOut Peak output value allowed. - */ - - void init( const int aLen, const CImageResizerVars& aVars, - const double aTrMul, const double aPkOut ) - { - Len = aLen; - Vars = &aVars; - LenE = aLen * Vars -> ElCount; - TrMul0 = aTrMul; - PkOut0 = aPkOut; - } - - /** - * @return "True" if dithering is recursive relative to scanlines meaning - * multi-threaded execution is not supported by this dithering method. - */ - - static bool isRecursive() - { - return( false ); - } - - /** - * Function performs rounding and clipping operations. - * - * @param ResScanline The buffer containing the final scanline. - */ - - void dither( fptype* const ResScanline ) const - { - const fptype c0 = (fptype) 0; - const fptype PkOut = (fptype) PkOut0; - int j; - - if( TrMul0 == 1.0 ) - { - // Optimization - do not perform bit depth truncation. - - for( j = 0; j < LenE; j++ ) - { - ResScanline[ j ] = clamp( round( ResScanline[ j ]), c0, - PkOut ); - } - } - else - { - const fptype TrMul = (fptype) TrMul0; - - for( j = 0; j < LenE; j++ ) - { - const fptype z0 = round( ResScanline[ j ] / TrMul ) * TrMul; - ResScanline[ j ] = clamp( z0, c0, PkOut ); - } - } - } - -protected: - int Len; ///< Scanline's length in pixels. - ///< - const CImageResizerVars* Vars; ///< Image resizing-related variables. - ///< - int LenE; ///< = LenE * ElCount. - ///< - double TrMul0; ///< Bit-depth truncation multiplier. - ///< - double PkOut0; ///< Peak output value allowed. - ///< -}; - -/** - * @brief Image resizer's error-diffusion dithering class, interleaved mode. - * - * This ditherer implements error-diffusion dithering which looks good, and - * whose results are compressed by PNG well. This implementation uses - * weighting coefficients obtained via machine optimization and visual - * evaluation. - * - * @tparam fptype Floating point type to use for storing pixel data. SIMD - * types can be used. - */ - -template< class fptype > -class CImageResizerDithererErrdINL : - public CImageResizerDithererDefINL< fptype > -{ -public: - /** - * Function initializes the ditherer object. - * - * @param aLen Scanline length in pixels to process. - * @param aVars Image resizing-related variables. - * @param aTrMul Bit-depth truncation multiplier. 1 - no additional - * truncation. - * @param aPkOut Peak output value allowed. - */ - - void init( const int aLen, const CImageResizerVars& aVars, - const double aTrMul, const double aPkOut ) - { - CImageResizerDithererDefINL< fptype > :: init( aLen, aVars, aTrMul, - aPkOut ); - - ResScanlineDith0.alloc( LenE + Vars -> ElCount, sizeof( fptype )); - ResScanlineDith = ResScanlineDith0 + Vars -> ElCount; - int i; - - for( i = 0; i < LenE + Vars -> ElCount; i++ ) - { - ResScanlineDith0[ i ] = (fptype) 0; - } - } - - static bool isRecursive() - { - return( true ); - } - - void dither( fptype* const ResScanline ) - { - const int ElCount = Vars -> ElCount; - const fptype c0 = (fptype) 0; - const fptype TrMul = (fptype) TrMul0; - const fptype PkOut = (fptype) PkOut0; - int j; - - for( j = 0; j < LenE; j++ ) - { - ResScanline[ j ] += ResScanlineDith[ j ]; - ResScanlineDith[ j ] = (fptype) 0; - } - - for( j = 0; j < LenE - ElCount; j++ ) - { - // Perform rounding, noise estimation and saturation. - - const fptype z0 = round( ResScanline[ j ] / TrMul ) * TrMul; - const fptype Noise = ResScanline[ j ] - z0; - ResScanline[ j ] = clamp( z0, c0, PkOut ); - - ResScanline[ j + ElCount ] += Noise * (fptype) 0.364842; - ResScanlineDith[ j - ElCount ] += Noise * (fptype) 0.207305; - ResScanlineDith[ j ] += Noise * (fptype) 0.364842; - ResScanlineDith[ j + ElCount ] += Noise * (fptype) 0.063011; - } - - while( j < LenE ) - { - const fptype z0 = round( ResScanline[ j ] / TrMul ) * TrMul; - const fptype Noise = ResScanline[ j ] - z0; - ResScanline[ j ] = clamp( z0, c0, PkOut ); - - ResScanlineDith[ j - ElCount ] += Noise * (fptype) 0.207305; - ResScanlineDith[ j ] += Noise * (fptype) 0.364842; - j++; - } - } - -protected: - using CImageResizerDithererDefINL< fptype > :: Len; - using CImageResizerDithererDefINL< fptype > :: Vars; - using CImageResizerDithererDefINL< fptype > :: LenE; - using CImageResizerDithererDefINL< fptype > :: TrMul0; - using CImageResizerDithererDefINL< fptype > :: PkOut0; - - CBuffer< fptype > ResScanlineDith0; ///< Error diffusion buffer. - ///< - fptype* ResScanlineDith; ///< Error diffusion buffer pointer which skips - ///< the first ElCount elements. - ///< -}; - -/** - * @brief Floating-point processing definition and abstraction class. - * - * This class defines several constants and typedefs that point to classes - * that should be used by the image resizing algorithm. Such "definition - * class" can be used to define alternative scanline processing algorithms - * (e.g. SIMD) and image scanline packing styles used during processing. This - * class also offers an abstraction layer for dithering, rounding and - * clamping (saturation) operation. - * - * The fpclass_def class can be used to define processing using both SIMD and - * non-SIMD types, but using algorithms that operate on interleaved pixels, - * and which are non-SIMD optimized themselves. - * - * @tparam afptype Floating point type to use for storing intermediate data - * and variables. For variables that are not used in intensive calculations - * the "double" type is always used. On the latest Intel processors (like - * i7-4770K) there is almost no performance difference between "double" and - * "float". Image quality differences between "double" and "float" are not - * apparent on 8-bit images. At the same time the "float" uses half amount of - * working memory the "double" type uses. SIMD types can be used. The - * functions round() and clamp() in the "avir" or other visible namespace - * should be available for the specified type. SIMD types allow to perform - * resizing of images with more than 4 channels, to be exact 4 * SIMD element - * number (e.g. 16 for float4), without modification of the image resizing - * algorithm required. - * @tparam afptypeatom The atomic type the "afptype" consists of. - * @tparam adith Ditherer class to use during processing. - */ - -template< class afptype, class afptypeatom = afptype, - class adith = CImageResizerDithererDefINL< afptype > > -class fpclass_def -{ -public: - typedef afptype fptype; ///< Floating-point type to use during processing. - ///< - typedef afptypeatom fptypeatom; ///< Atomic type "fptype" consists of. - ///< - static const int fppack = sizeof( fptype ) / sizeof( fptypeatom ); ///< - ///< The number of atomic types stored in a single "fptype" element. - ///< - static const int fpalign = sizeof( fptype ); ///< Suggested alignment size - ///< in bytes. This is not a required alignment, because image - ///< resizing algorithm cannot be made to have a strictly aligned data - ///< access at all steps (e.g. interpolation cannot perform aligned - ///< accesses). - ///< - static const int elalign = 1; ///< Length alignment of arrays of elements. - ///< This applies to filters and intermediate buffers: this constant - ///< forces filters and scanlines to have a length which is a multiple - ///< of this value, for more efficient SIMD implementation. - ///< - static const int packmode = 0; ///< 0 if interleaved packing, 1 if - ///< de-interleaved. - ///< - typedef CImageResizerFilterStepINL< fptype, fptypeatom > CFilterStep; ///< - ///< Filtering step class to use during processing. - ///< - typedef adith CDitherer; ///< Ditherer class to use during processing. - ///< -}; - -/** - * @brief Image resizer class. - * - * The object of this class can be used to resize 1-4 channel images to any - * required size. Resizing is performed by utilizing interpolated sinc - * fractional delay filters plus (if necessary) a cascade of built-in - * sinc function-based 2X upsampling or 2X downsampling stages, followed by a - * correction filtering. - * - * Object of this class can be allocated on stack. - * - * @tparam fpclass Floating-point processing definition class to use. See - * avir::fpclass_def for more details. - */ - -template< class fpclass = fpclass_def< float > > -class CImageResizer -{ - AVIR_NOCTOR( CImageResizer ); - -public: - /** - * Constructor initializes the resizer. - * - * @param aResBitDepth Required bit depth of resulting image (1-16). If - * integer value output is used (e.g. uint8_t), the bit depth also affects - * rounding: for example, if aResBitDepth=6 and "Tout" is uint8_t, the - * result will be rounded to 6 most significant bits (2 least significant - * bits truncated, with dithering applied). - * @param aSrcBitDepth Source image's real bit-depth. Set to 0 to use - * aResBitDepth. - * @param aParams Resizing algorithm's parameters to use. Leave out for - * default values. Can be useful when performing automatic optimization of - * parameters. - */ - - CImageResizer( const int aResBitDepth = 8, const int aSrcBitDepth = 0, - const CImageResizerParams& aParams = CImageResizerParamsDef() ) - : Params( aParams ) - , ResBitDepth( aResBitDepth ) - { - SrcBitDepth = ( aSrcBitDepth == 0 ? ResBitDepth : aSrcBitDepth ); - - initFilterBank( FixedFilterBank, 1.0, false, CFltBuffer() ); - FixedFilterBank.createAllFilters(); - } - - /** - * Function resizes image. - * - * @param SrcBuf Source image buffer. - * @param SrcWidth Source image width. - * @param SrcHeight Source image height. - * @param SrcScanlineSize Physical size of source scanline in elements - * (not bytes). If this value is below 1, SrcWidth * ElCountIO will be - * used as the physical source scanline size. - * @param[out] NewBuf Buffer to accept the resized image. Can be equal to - * SrcBuf if the size of the resized image is smaller or equal to source - * image in size. - * @param NewWidth New image width. - * @param NewHeight New image height. - * @param ElCountIO The number of elements (channels) used to store each - * source and destination pixel (1-4). - * @param k Resizing step (one output pixel corresponds to "k" input - * pixels). A downsizing factor if > 1.0; upsizing factor if <= 1.0. - * Multiply by -1 if you would like to bypass "ox" and "oy" adjustment - * which is done by default to produce a centered image. If step value - * equals 0, the step value will be chosen automatically and independently - * for horizontal and vertical resizing. - * @param[in,out] aVars Pointer to variables structure to be passed to the - * image resizing function. Can be NULL. Only variables that are - * initialized in default constructor of this structure are accepted by - * this function. These variables will not be changed by this function. - * All other variables can be modified by this function. The access to - * this object is not thread-safe, each concurrent instance of this - * function should use a separate aVars object. - * @tparam Tin Input buffer element's type. Can be uint8_t (0-255 value - * range), uint16_t (0-65535 value range), float (0.0-1.0 value range), - * double (0.0-1.0 value range). Larger integer types are treated as - * uint16_t. Signed integer types are unsupported. - * @tparam Tout Output buffer element's type. Can be uint8_t (0-255 value - * range), uint16_t (0-65535 value range), float (0.0-1.0 value range), - * double (0.0-1.0 value range). Larger integer types are treated as - * uint16_t. Signed integer types are unsupported. - */ - - template< class Tin, class Tout > - void resizeImage( const Tin* const SrcBuf, const int SrcWidth, - const int SrcHeight, int SrcScanlineSize, Tout* const NewBuf, - const int NewWidth, const int NewHeight, const int ElCountIO, - const double k, CImageResizerVars* const aVars = NULL ) const - { - if( SrcWidth == 0 || SrcHeight == 0 ) - { - memset( NewBuf, 0, (size_t) NewWidth * NewHeight * - sizeof( Tout )); - - return; - } - else - if( NewWidth == 0 || NewHeight == 0 ) - { - return; - } - - CImageResizerVars DefVars; - CImageResizerVars& Vars = ( aVars == NULL ? DefVars : *aVars ); - - CImageResizerThreadPool DefThreadPool; - CImageResizerThreadPool& ThreadPool = ( Vars.ThreadPool == NULL ? - DefThreadPool : *Vars.ThreadPool ); - - // Define resizing steps, also optionally modify offsets so that - // resizing produces a "centered" image. - - double kx; - double ky; - double ox = Vars.ox; - double oy = Vars.oy; - - if( k == 0.0 ) - { - kx = (double) SrcWidth / NewWidth; - ox += ( kx - 1.0 ) * 0.5; - - ky = (double) SrcHeight / NewHeight; - oy += ( ky - 1.0 ) * 0.5; - } - else - if( k > 0.0 ) - { - kx = k; - ky = k; - - const double ko = ( k - 1.0 ) * 0.5; - ox += ko; - oy += ko; - } - else - { - kx = -k; - ky = -k; - } - - // Evaluate pre-multipliers used on the output stage. - - const bool IsInFloat = ( (Tin) 0.25 != 0 ); - const bool IsOutFloat = ( (Tout) 0.25 != 0 ); - double OutMul; // Output multiplier. - - if( Vars.UseSRGBGamma ) - { - if( IsInFloat ) - { - Vars.InGammaMult = 1.0; - } - else - { - Vars.InGammaMult = - 1.0 / ( sizeof( Tin ) == 1 ? 255.0 : 65535.0 ); - } - - if( IsOutFloat ) - { - Vars.OutGammaMult = 1.0; - } - else - { - Vars.OutGammaMult = ( sizeof( Tout ) == 1 ? 255.0 : 65535.0 ); - } - - OutMul = 1.0; - } - else - { - if( IsOutFloat ) - { - OutMul = 1.0; - } - else - { - OutMul = ( sizeof( Tout ) == 1 ? 255.0 : 65535.0 ); - } - - if( !IsInFloat ) - { - OutMul /= ( sizeof( Tin ) == 1 ? 255.0 : 65535.0 ); - } - } - - // Fill widely-used variables. - - const int ElCount = ( ElCountIO + fpclass :: fppack - 1 ) / - fpclass :: fppack; - - const int NewWidthE = NewWidth * ElCount; - - if( SrcScanlineSize < 1 ) - { - SrcScanlineSize = SrcWidth * ElCountIO; - } - - Vars.ElCount = ElCount; - Vars.ElCountIO = ElCountIO; - Vars.fppack = fpclass :: fppack; - Vars.fpalign = fpclass :: fpalign; - Vars.elalign = fpclass :: elalign; - Vars.packmode = fpclass :: packmode; - - // Horizontal scanline filtering and resizing. - - CDSPFracFilterBankLin< fptype > FltBank; - CFilterSteps FltSteps; - typename CFilterStep :: CRPosBufArray RPosBufArray; - CBuffer< uint8_t > UsedFracMap; - - // Perform the filtering steps modeling at various modes, find the - // most efficient mode for both horizontal and vertical resizing. - - int UseBuildMode = 1; - const int BuildModeCount = - ( FixedFilterBank.getOrder() == 0 ? 4 : 2 ); - - int m; - - if( Vars.BuildMode >= 0 ) - { - UseBuildMode = Vars.BuildMode; - } - else - { - int BestScore = 0x7FFFFFFF; - - for( m = 0; m < BuildModeCount; m++ ) - { - CDSPFracFilterBankLin< fptype > TmpBank; - CFilterSteps TmpSteps; - Vars.k = kx; - Vars.o = ox; - buildFilterSteps( TmpSteps, Vars, TmpBank, OutMul, m, true ); - updateFilterStepBuffers( TmpSteps, Vars, RPosBufArray, - SrcWidth, NewWidth ); - - fillUsedFracMap( TmpSteps[ Vars.ResizeStep ], UsedFracMap ); - const int c = calcComplexity( TmpSteps, Vars, UsedFracMap, - SrcHeight ); - - if( c < BestScore ) - { - UseBuildMode = m; - BestScore = c; - } - } - } - - // Perform the actual filtering steps building. - - Vars.k = kx; - Vars.o = ox; - buildFilterSteps( FltSteps, Vars, FltBank, OutMul, UseBuildMode, - false ); - - updateFilterStepBuffers( FltSteps, Vars, RPosBufArray, SrcWidth, - NewWidth ); - - updateBufLenAndRPosPtrs( FltSteps, Vars, NewWidth ); - - const int ThreadCount = ThreadPool.getSuggestedWorkloadCount(); - // Includes the current thread. - - CStructArray< CThreadData< Tin, Tout > > td; - td.setItemCount( ThreadCount ); - int i; - - for( i = 0; i < ThreadCount; i++ ) - { - if( i > 0 ) - { - ThreadPool.addWorkload( &td[ i ]); - } - - td[ i ].init( i, ThreadCount, FltSteps, Vars ); - - td[ i ].initScanlineQueue( td[ i ].sopResizeH, SrcHeight, - SrcWidth ); - } - - CBuffer< fptype, size_t > FltBuf( (size_t) NewWidthE * SrcHeight, - fpclass :: fpalign ); // Temporary buffer that receives - // horizontally-filtered and resized image. - - for( i = 0; i < SrcHeight; i++ ) - { - td[ i % ThreadCount ].addScanlineToQueue( - (void*) &SrcBuf[ (size_t) i * SrcScanlineSize ], - &FltBuf[ (size_t) i * NewWidthE ]); - } - - ThreadPool.startAllWorkloads(); - td[ 0 ].processScanlineQueue(); - ThreadPool.waitAllWorkloadsToFinish(); - - // Vertical scanline filtering and resizing, reuse previously defined - // filtering steps if possible. - - const int PrevUseBuildMode = UseBuildMode; - - if( Vars.BuildMode >= 0 ) - { - UseBuildMode = Vars.BuildMode; - } - else - { - CImageResizerVars TmpVars( Vars ); - int BestScore = 0x7FFFFFFF; - - for( m = 0; m < BuildModeCount; m++ ) - { - CDSPFracFilterBankLin< fptype > TmpBank; - TmpBank.copyInitParams( FltBank ); - CFilterSteps TmpSteps; - TmpVars.k = ky; - TmpVars.o = oy; - buildFilterSteps( TmpSteps, TmpVars, TmpBank, 1.0, m, true ); - updateFilterStepBuffers( TmpSteps, TmpVars, RPosBufArray, - SrcHeight, NewHeight ); - - fillUsedFracMap( TmpSteps[ TmpVars.ResizeStep ], - UsedFracMap ); - - const int c = calcComplexity( TmpSteps, TmpVars, UsedFracMap, - NewWidth ); - - if( c < BestScore ) - { - UseBuildMode = m; - BestScore = c; - } - } - } - - Vars.k = ky; - Vars.o = oy; - - if( UseBuildMode == PrevUseBuildMode && ky == kx ) - { - if( OutMul != 1.0 ) - { - modifyCorrFilterDCGain( FltSteps, 1.0 / OutMul ); - } - } - else - { - buildFilterSteps( FltSteps, Vars, FltBank, 1.0, UseBuildMode, - false ); - } - - updateFilterStepBuffers( FltSteps, Vars, RPosBufArray, SrcHeight, - NewHeight ); - - updateBufLenAndRPosPtrs( FltSteps, Vars, NewWidth ); - - if( IsOutFloat && sizeof( FltBuf[ 0 ]) == sizeof( Tout ) && - fpclass :: packmode == 0 ) - { - // In-place output. - - for( i = 0; i < ThreadCount; i++ ) - { - td[ i ].initScanlineQueue( td[ i ].sopResizeV, NewWidth, - SrcHeight, NewWidthE, NewWidthE ); - } - - for( i = 0; i < NewWidth; i++ ) - { - td[ i % ThreadCount ].addScanlineToQueue( - &FltBuf[ (size_t) i * ElCount ], - (fptype*) &NewBuf[ (size_t) i * ElCount ]); - } - - ThreadPool.startAllWorkloads(); - td[ 0 ].processScanlineQueue(); - ThreadPool.waitAllWorkloadsToFinish(); - ThreadPool.removeAllWorkloads(); - - return; - } - - CBuffer< fptype, size_t > ResBuf( (size_t) NewWidthE * NewHeight, - fpclass :: fpalign ); - - for( i = 0; i < ThreadCount; i++ ) - { - td[ i ].initScanlineQueue( td[ i ].sopResizeV, NewWidth, - SrcHeight, NewWidthE, NewWidthE ); - } - - const int im = ( fpclass :: packmode == 0 ? ElCount : 1 ); - - for( i = 0; i < NewWidth; i++ ) - { - td[ i % ThreadCount ].addScanlineToQueue( - &FltBuf[ (size_t) i * im ], &ResBuf[ (size_t) i * im ]); - } - - ThreadPool.startAllWorkloads(); - td[ 0 ].processScanlineQueue(); - ThreadPool.waitAllWorkloadsToFinish(); - - if( IsOutFloat ) - { - // Perform output, but skip dithering. - - for( i = 0; i < ThreadCount; i++ ) - { - td[ i ].initScanlineQueue( td[ i ].sopUnpackH, - NewHeight, NewWidth ); - } - - for( i = 0; i < NewHeight; i++ ) - { - td[ i % ThreadCount ].addScanlineToQueue( - &ResBuf[ (size_t) i * NewWidthE ], - &NewBuf[ (size_t) i * NewWidth * ElCountIO ]); - } - - ThreadPool.startAllWorkloads(); - td[ 0 ].processScanlineQueue(); - ThreadPool.waitAllWorkloadsToFinish(); - ThreadPool.removeAllWorkloads(); - - return; - } - - // Perform output with dithering (for integer output only). - - int TruncBits; // The number of lower bits to truncate and dither. - int OutRange; // Output range. - - if( sizeof( Tout ) == 1 ) - { - TruncBits = 8 - ResBitDepth; - OutRange = 255; - } - else - { - TruncBits = 16 - ResBitDepth; - OutRange = 65535; - } - - const double PkOut = OutRange; - const double TrMul = ( TruncBits > 0 ? - PkOut / ( OutRange >> TruncBits ) : 1.0 ); - - if( CDitherer :: isRecursive() ) - { - td[ 0 ].getDitherer().init( NewWidth, Vars, TrMul, PkOut ); - - if( Vars.UseSRGBGamma ) - { - for( i = 0; i < NewHeight; i++ ) - { - fptype* const ResScanline = - &ResBuf[ (size_t) i * NewWidthE ]; - - CFilterStep :: applySRGBGamma( ResScanline, NewWidth, - Vars ); - - td[ 0 ].getDitherer().dither( ResScanline ); - - CFilterStep :: unpackScanline( ResScanline, - &NewBuf[ (size_t) i * NewWidth * ElCountIO ], - NewWidth, Vars ); - } - } - else - { - for( i = 0; i < NewHeight; i++ ) - { - fptype* const ResScanline = - &ResBuf[ (size_t) i * NewWidthE ]; - - td[ 0 ].getDitherer().dither( ResScanline ); - - CFilterStep :: unpackScanline( ResScanline, - &NewBuf[ (size_t) i * NewWidth * ElCountIO ], - NewWidth, Vars ); - } - } - } - else - { - for( i = 0; i < ThreadCount; i++ ) - { - td[ i ].initScanlineQueue( td[ i ].sopDitherAndUnpackH, - NewHeight, NewWidth ); - - td[ i ].getDitherer().init( NewWidth, Vars, TrMul, PkOut ); - } - - for( i = 0; i < NewHeight; i++ ) - { - td[ i % ThreadCount ].addScanlineToQueue( - &ResBuf[ (size_t) i * NewWidthE ], - &NewBuf[ (size_t) i * NewWidth * ElCountIO ]); - } - - ThreadPool.startAllWorkloads(); - td[ 0 ].processScanlineQueue(); - ThreadPool.waitAllWorkloadsToFinish(); - } - - ThreadPool.removeAllWorkloads(); - } - -private: - typedef typename fpclass :: fptype fptype; ///< Floating-point type to use - ///< during processing. - ///< - typedef typename fpclass :: CFilterStep CFilterStep; ///< Filtering step - ///< class to use during processing. - ///< - typedef typename fpclass :: CDitherer CDitherer; ///< Ditherer class to - ///< use during processing. - ///< - CImageResizerParams Params; ///< Algorithm's parameters currently in use. - ///< - int SrcBitDepth; ///< Bit resolution of the source image. - ///< - int ResBitDepth; ///< Bit resolution of the resulting image. - ///< - CDSPFracFilterBankLin< fptype > FixedFilterBank; ///< Fractional delay - ///< filter bank with fixed characteristics, mainly for upsizing - ///< cases. - ///< - - /** - * @brief Filtering steps array. - * - * The object of this class stores filtering steps together. - */ - - typedef CStructArray< CFilterStep > CFilterSteps; - - /** - * Function initializes the filter bank in the specified resizing step - * according to the source and resulting image bit depths. - * - * @param FltBank Filter bank to initialize. - * @param CutoffMult Cutoff multiplier, 0 to 1. 1 corresponds to 0.5pi - * cutoff point. - * @param ForceHiOrder "True" if a high-order interpolation should be - * forced which requires considerably less resources for initialization. - * @param ExtFilter External filter to apply to interpolation filter. - */ - - void initFilterBank( CDSPFracFilterBankLin< fptype >& FltBank, - const double CutoffMult, const bool ForceHiOrder, - const CFltBuffer& ExtFilter ) const - { - const int IntBitDepth = ( ResBitDepth > SrcBitDepth ? ResBitDepth : - SrcBitDepth ); - - const double SNR = -6.02 * ( IntBitDepth + 3 ); - int UseOrder; - int FracCount; // The number of fractional delay filters sampled by - // the filter bank. This variable affects the signal-to-noise - // ratio at interpolation stage. Theoretically, at UseOrder==1, - // 8-bit image resizing requires 66.2 dB SNR or 11. 16-bit - // resizing requires 114.4 dB SNR or 150. At UseOrder=0 the - // required number of filters is exponentially higher. - - if( ForceHiOrder || IntBitDepth > 8 ) - { - UseOrder = 1; // -146 dB max - FracCount = (int) ceil( 0.23134052 * exp( -0.058062929 * SNR )); - } - else - { - UseOrder = 0; // -72 dB max - FracCount = (int) ceil( 0.33287686 * exp( -0.11334583 * SNR )); - } - - if( FracCount < 2 ) - { - FracCount = 2; - } - - FltBank.init( FracCount, UseOrder, Params.IntFltLen / CutoffMult, - Params.IntFltCutoff * CutoffMult, Params.IntFltAlpha, ExtFilter, - fpclass :: fpalign, fpclass :: elalign ); - } - - /** - * Function allocates filter buffer taking "fpclass" alignments into - * account. The allocated buffer may be larger than the requested size: in - * this case the additional elements will be zeroed by this function. - * - * @param Flt Filter buffer. - * @param ReqCapacity The required filter buffer's capacity. - * @param IsModel "True" if filtering steps modeling is performed without - * actual filter allocation. - * @param FltExt If non-NULL this variable will receive the number of - * elements the filter was extended by. - */ - - static void allocFilter( CBuffer< fptype >& Flt, const int ReqCapacity, - const bool IsModel = false, int* const FltExt = NULL ) - { - int UseCapacity = ( ReqCapacity + fpclass :: elalign - 1 ) & - ~( fpclass :: elalign - 1 ); - - int Ext = UseCapacity - ReqCapacity; - - if( FltExt != NULL ) - { - *FltExt = Ext; - } - - if( IsModel ) - { - Flt.forceCapacity( UseCapacity ); - return; - } - - Flt.alloc( UseCapacity, fpclass :: fpalign ); - - while( Ext > 0 ) - { - Ext--; - Flt[ ReqCapacity + Ext ] = (fptype) 0; - } - } - - /** - * Function assigns filter parameters to the specified filtering step - * object. - * - * @param fs Filtering step to assign parameter to. This step cannot be - * the last step if ResampleFactor greater than 1 was specified. - * @param IsUpsample "True" if upsampling step. Should be set to "false" - * if FltCutoff is negative. - * @param ResampleFactor Resampling factor of this filter (>=1). - * @param FltCutoff Filter cutoff point. This value will be divided by the - * ResampleFactor if IsUpsample equals "true". If zero value was - * specified, the "half-band" predefined filter will be created. In this - * case the ResampleFactor will modify the filter cutoff point. - * @param DCGain DC gain to apply to the filter. Assigned to filtering - * step's DCGain variable. - * @param UseFltOrig "True" if the originally-designed filter should be - * left in filtering step's FltOrig buffer. Otherwise it will be freed. - * @param IsModel "True" if filtering steps modeling is performed without - * actual filter building. - */ - - void assignFilterParams( CFilterStep& fs, const bool IsUpsample, - const int ResampleFactor, const double FltCutoff, const double DCGain, - const bool UseFltOrig, const bool IsModel ) const - { - double FltAlpha; - double Len2; - double Freq; - - if( FltCutoff == 0.0 ) - { - const double m = 2.0 / ResampleFactor; - FltAlpha = Params.HBFltAlpha; - Len2 = 0.5 * Params.HBFltLen / m; - Freq = AVIR_PI * Params.HBFltCutoff * m; - } - else - { - FltAlpha = Params.LPFltAlpha; - Len2 = 0.25 * Params.LPFltBaseLen / FltCutoff; - Freq = AVIR_PI * Params.LPFltCutoffMult * FltCutoff; - } - - if( IsUpsample ) - { - Len2 *= ResampleFactor; - Freq /= ResampleFactor; - fs.DCGain = DCGain * ResampleFactor; - } - else - { - fs.DCGain = DCGain; - } - - fs.FltOrig.Len2 = Len2; - fs.FltOrig.Freq = Freq; - fs.FltOrig.Alpha = FltAlpha; - fs.FltOrig.DCGain = fs.DCGain; - - CDSPPeakedCosineLPF w( Len2, Freq, FltAlpha ); - - fs.IsUpsample = IsUpsample; - fs.ResampleFactor = ResampleFactor; - fs.FltLatency = w.fl2; - - int FltExt; // Filter's extension due to fpclass :: elalign. - - if( IsModel ) - { - allocFilter( fs.Flt, w.FilterLen, true, &FltExt ); - - if( UseFltOrig ) - { - // Allocate a real buffer even in modeling mode since this - // filter may be copied by the filter bank. - - fs.FltOrig.alloc( w.FilterLen ); - memset( &fs.FltOrig[ 0 ], 0, - w.FilterLen * sizeof( fs.FltOrig[ 0 ])); - } - } - else - { - fs.FltOrig.alloc( w.FilterLen ); - - w.generateLPF( &fs.FltOrig[ 0 ], fs.DCGain ); - - allocFilter( fs.Flt, fs.FltOrig.getCapacity(), false, &FltExt ); - copyArray( &fs.FltOrig[ 0 ], &fs.Flt[ 0 ], - fs.FltOrig.getCapacity() ); - - if( !UseFltOrig ) - { - fs.FltOrig.free(); - } - } - - if( IsUpsample ) - { - int l = fs.Flt.getCapacity() - fs.FltLatency - ResampleFactor - - FltExt; - - allocFilter( fs.PrefixDC, l, IsModel ); - allocFilter( fs.SuffixDC, fs.FltLatency, IsModel ); - - if( IsModel ) - { - return; - } - - // Create prefix and suffix "tails" used during upsampling. - - const fptype* ip = &fs.Flt[ fs.FltLatency + ResampleFactor ]; - copyArray( ip, &fs.PrefixDC[ 0 ], l ); - - while( true ) - { - ip += ResampleFactor; - l -= ResampleFactor; - - if( l <= 0 ) - { - break; - } - - addArray( ip, &fs.PrefixDC[ 0 ], l ); - } - - l = fs.FltLatency; - fptype* op = &fs.SuffixDC[ 0 ]; - copyArray( &fs.Flt[ 0 ], op, l ); - - while( true ) - { - op += ResampleFactor; - l -= ResampleFactor; - - if( l <= 0 ) - { - break; - } - - addArray( &fs.Flt[ 0 ], op, l ); - } - } - else - if( !UseFltOrig ) - { - fs.EdgePixelCount = fs.EdgePixelCountDef; - } - } - - /** - * Function adds a correction filter that tries to achieve a linear - * frequency response at all frequencies. The actual resulting response - * may feature a slight damping of the highest frequencies since a - * suitably short correction filter cannot fix steep high-frequency - * damping. - * - * This function assumes that the resizing step is currently the last - * step, even if it was not inserted yet: this allows placement of the - * correction filter both before and after the resizing step. - * - * @param Steps Filtering steps. - * @param bw Resulting bandwidth relative to the original bandwidth (which - * is 1.0), usually 1/k. Should be <= 1.0. - * @param IsPreCorrection "True" if the filtering step was already created - * and it is first in the Steps array. "True" also adds edge pixels to - * reduce edge artifacts. - * @param IsModel "True" if filtering steps modeling is performed without - * actual filter building. - */ - - void addCorrectionFilter( CFilterSteps& Steps, const double bw, - const bool IsPreCorrection, const bool IsModel ) const - { - CFilterStep& fs = ( IsPreCorrection ? Steps[ 0 ] : Steps.add() ); - fs.IsUpsample = false; - fs.ResampleFactor = 1; - fs.DCGain = 1.0; - fs.EdgePixelCount = ( IsPreCorrection ? fs.EdgePixelCountDef : 0 ); - - if( IsModel ) - { - allocFilter( fs.Flt, CDSPFIREQ :: calcFilterLength( - Params.CorrFltLen, fs.FltLatency ), true ); - - return; - } - - const int BinCount = 65; // Frequency response bins to control. - const int BinCount1 = BinCount - 1; - double curbw = 1.0; // Bandwidth of the filter at the current step. - int i; - int j; - double re; - double im; - - CBuffer< double > Bins( BinCount ); // Adjustment introduced by all - // steps at all frequencies of interest. - - for( j = 0; j < BinCount; j++ ) - { - Bins[ j ] = 1.0; - } - - const int si = ( IsPreCorrection ? 1 : 0 ); - - for( i = si; i < Steps.getItemCount() - ( si ^ 1 ); i++ ) - { - const CFilterStep& fs = Steps[ i ]; - - if( fs.IsUpsample ) - { - curbw *= fs.ResampleFactor; - - if( fs.FltOrig.getCapacity() > 0 ) - { - continue; - } - } - - const fptype* Flt; - int FltLen; - - if( fs.ResampleFactor == 0 ) - { - Flt = fs.FltBank -> getFilter( 0 ); - FltLen = fs.FltBank -> getFilterLen(); - } - else - { - Flt = &fs.Flt[ 0 ]; - FltLen = fs.Flt.getCapacity(); - } - - // Calculate frequency response adjustment introduced by the - // filter at this step, within the bounds of bandwidth of - // interest. - - const double thm = AVIR_PI * bw / ( curbw * BinCount1 ); - - for( j = 0; j < BinCount; j++ ) - { - calcFIRFilterResponse( Flt, FltLen, j * thm, re, im ); - - Bins[ j ] *= fs.DCGain / sqrt( re * re + im * im ); - } - - if( !fs.IsUpsample && fs.ResampleFactor > 1 ) - { - curbw /= fs.ResampleFactor; - } - } - - // Calculate filter. - - CDSPFIREQ EQ; - EQ.init( bw * 2.0, Params.CorrFltLen, BinCount, 0.0, bw, false, - Params.CorrFltAlpha ); - - fs.FltLatency = EQ.getFilterLatency(); - - CBuffer< double > Filter( EQ.getFilterLength() ); - EQ.buildFilter( Bins, &Filter[ 0 ]); - normalizeFIRFilter( &Filter[ 0 ], Filter.getCapacity(), 1.0 ); - - allocFilter( fs.Flt, Filter.getCapacity() ); - copyArray( &Filter[ 0 ], &fs.Flt[ 0 ], Filter.getCapacity() ); - - // Print a theoretically achieved final frequency response at various - // feature sizes (from DC to 1 pixel). Values above 255 means features - // become brighter, values below 255 means features become dimmer. - -/* const double sbw = ( bw > 1.0 ? 1.0 / bw : 1.0 ); - - for( j = 0; j < BinCount; j++ ) - { - const double th = AVIR_PI * sbw * j / BinCount1; - - calcFIRFilterResponse( &fs.Flt[ 0 ], fs.Flt.getCapacity(), - th, re, im ); - - printf( "%f\n", sqrt( re * re + im * im ) / Bins[ j ] * 255 ); - } - - printf( "***\n" );*/ - } - - /** - * Function adds a sharpening filter if image is being upsized. Such - * sharpening allows to spot interpolation filter's stop-band attenuation: - * if attenuation is too weak, a "dark grid" and other artifacts may - * become visible. - * - * It is assumed that 40 decibel stop-band attenuation should be - * considered a required minimum: this allows application of (deliberately - * strong) 64X sharpening without spotting any artifacts. - * - * @param Steps Filtering steps. - * @param bw Resulting bandwidth relative to the original bandwidth (which - * is 1.0), usually 1/k. - * @param IsModel "True" if filtering steps modeling is performed without - * actual filter building. - */ - - static void addSharpenTest( CFilterSteps& Steps, const double bw, - const bool IsModel ) - { - if( bw <= 1.0 ) - { - return; - } - - const double FltLen = 10.0 * bw; - - CFilterStep& fs = Steps.add(); - fs.IsUpsample = false; - fs.ResampleFactor = 1; - fs.DCGain = 1.0; - fs.EdgePixelCount = 0; - - if( IsModel ) - { - allocFilter( fs.Flt, CDSPFIREQ :: calcFilterLength( FltLen, - fs.FltLatency ), true ); - - return; - } - - const int BinCount = 200; - CBuffer< double > Bins( BinCount ); - int Thresh = (int) round( BinCount / bw * 1.75 ); - - if( Thresh > BinCount ) - { - Thresh = BinCount; - } - - int j; - - for( j = 0; j < Thresh; j++ ) - { - Bins[ j ] = 1.0; - } - - for( j = Thresh; j < BinCount; j++ ) - { - Bins[ j ] = 256.0; - } - - CDSPFIREQ EQ; - EQ.init( bw * 2.0, FltLen, BinCount, 0.0, bw, false, 1.7 ); - - fs.FltLatency = EQ.getFilterLatency(); - - CBuffer< double > Filter( EQ.getFilterLength() ); - EQ.buildFilter( Bins, &Filter[ 0 ]); - normalizeFIRFilter( &Filter[ 0 ], Filter.getCapacity(), 1.0 ); - - allocFilter( fs.Flt, Filter.getCapacity() ); - copyArray( &Filter[ 0 ], &fs.Flt[ 0 ], Filter.getCapacity() ); - -/* for( j = 0; j < BinCount; j++ ) - { - const double th = AVIR_PI * j / ( BinCount - 1 ); - double re; - double im; - - calcFIRFilterResponse( &fs.Flt[ 0 ], fs.Flt.getCapacity(), - th, re, im ); - - printf( "%f\n", sqrt( re * re + im * im )); - } - - printf( "***\n" );*/ - } - - /** - * Function builds sequence of filtering steps depending on the specified - * resizing coefficient. The last steps included are always the resizing - * step then (possibly) the correction step. - * - * @param Steps Array that receives filtering steps. - * @param[out] Vars Variables object. - * @param FltBank Filter bank to initialize and use. - * @param DCGain The overall DC gain to apply. This DC gain is applied to - * the first filtering step only (upsampling or filtering step). - * @param ModeFlags Build mode flags to use. This is a bitmap of switches - * that enable or disable certain algorithm features. - * @param IsModel "True" if filtering steps modeling is performed without - * the actual filter allocation and building. - */ - - void buildFilterSteps( CFilterSteps& Steps, CImageResizerVars& Vars, - CDSPFracFilterBankLin< fptype >& FltBank, const double DCGain, - const int ModeFlags, const bool IsModel ) const - { - Steps.clear(); - - const bool DoFltAndIntCombo = (( ModeFlags & 1 ) != 0 ); // Do filter - // and interpolator combining. - const bool ForceHiOrderInt = (( ModeFlags & 2 ) != 0 ); // Force use - // of a higher-order interpolation. - const bool UseHalfband = (( ModeFlags & 4 ) != 0 ); // Use half-band - // filter. - - const double bw = 1.0 / Vars.k; // Resulting bandwidth. - const int UpsampleFactor = ( (int) floor( Vars.k ) < 2 ? 2 : 1 ); - double IntCutoffMult; // Interpolation filter cutoff multiplier. - CFilterStep* ReuseStep; // If not NULL, resizing step should use - // this step object instead of creating a new one. - CFilterStep* ExtFltStep; // Use FltOrig of this step as the external - // filter to applied to the interpolator. - bool IsPreCorrection; // "True" if the correction filter is applied - // first. - double FltCutoff; // Cutoff frequency of the first filtering step. - double corrbw; ///< Bandwidth at the correction step. - - if( Vars.k <= 1.0 ) - { - IsPreCorrection = true; - FltCutoff = 1.0; - corrbw = 1.0; - Steps.add(); - } - else - { - IsPreCorrection = false; - FltCutoff = bw; - corrbw = bw; - } - - // Add 1 upsampling or several downsampling filters. - - if( UpsampleFactor > 1 ) - { - CFilterStep& fs = Steps.add(); - assignFilterParams( fs, true, UpsampleFactor, FltCutoff, DCGain, - DoFltAndIntCombo, IsModel ); - - IntCutoffMult = FltCutoff * 2.0 / UpsampleFactor; - ReuseStep = NULL; - ExtFltStep = ( DoFltAndIntCombo ? &fs : NULL ); - } - else - { - int DownsampleFactor; - - while( true ) - { - DownsampleFactor = (int) floor( 0.5 / FltCutoff ); - bool DoHBFltAdd = ( UseHalfband && DownsampleFactor > 1 ); - - if( DoHBFltAdd ) - { - assignFilterParams( Steps.add(), false, DownsampleFactor, - 0.0, 1.0, false, IsModel ); - - FltCutoff *= DownsampleFactor; - } - else - { - if( DownsampleFactor < 1 ) - { - DownsampleFactor = 1; - } - - break; - } - } - - CFilterStep& fs = Steps.add(); - assignFilterParams( fs, false, DownsampleFactor, FltCutoff, - DCGain, DoFltAndIntCombo, IsModel ); - - IntCutoffMult = FltCutoff / 0.5; - - if( DoFltAndIntCombo ) - { - ReuseStep = &fs; - ExtFltStep = &fs; - } - else - { - IntCutoffMult *= DownsampleFactor; - ReuseStep = NULL; - ExtFltStep = NULL; - } - } - - // Insert resizing and correction steps. - - CFilterStep& fs = ( ReuseStep == NULL ? Steps.add() : *ReuseStep ); - - Vars.ResizeStep = Steps.getItemCount() - 1; - fs.IsUpsample = false; - fs.ResampleFactor = 0; - fs.DCGain = ( ExtFltStep == NULL ? 1.0 : ExtFltStep -> DCGain ); - - initFilterBank( FltBank, IntCutoffMult, ForceHiOrderInt, - ( ExtFltStep == NULL ? fs.FltOrig : ExtFltStep -> FltOrig )); - - if( FltBank == FixedFilterBank ) - { - fs.FltBank = (CDSPFracFilterBankLin< fptype >*) &FixedFilterBank; - } - else - { - fs.FltBank = &FltBank; - } - - addCorrectionFilter( Steps, corrbw, IsPreCorrection, IsModel ); - - //addSharpenTest( Steps, bw, IsModel ); - } - - /** - * Function extends *this upsampling step so that it produces more - * upsampled pixels that cover the prefix and suffix needs of the next - * step. After the call to this function the InPrefix and InSuffix - * variables of the next step will be set to zero. - * - * @param fs Upsampling filtering step. - * @param NextStep The next step structure. - */ - - static void extendUpsample( CFilterStep& fs, CFilterStep& NextStep ) - { - fs.InPrefix = ( NextStep.InPrefix + fs.ResampleFactor - 1 ) / - fs.ResampleFactor; - - fs.OutPrefix += fs.InPrefix * fs.ResampleFactor; - NextStep.InPrefix = 0; - - fs.InSuffix = ( NextStep.InSuffix + fs.ResampleFactor - 1 ) / - fs.ResampleFactor; - - fs.OutSuffix += fs.InSuffix * fs.ResampleFactor; - NextStep.InSuffix = 0; - } - - /** - * Function fills resizing step's RPosBuf array, excluding the actual - * "ftp" pointers and "SrcOffs" offsets. - * - * This array should be cleared if the resizing step or offset were - * changed. Otherwise this function only fills the elements required to - * cover resizing step's OutLen. - * - * This function is called by the updateFilterStepBuffers() function. - * - * @param fs Resizing step. - * @param Vars Variables object. - */ - - static void fillRPosBuf( CFilterStep& fs, const CImageResizerVars& Vars ) - { - const int PrevLen = fs.RPosBuf -> getCapacity(); - - if( fs.OutLen > PrevLen ) - { - fs.RPosBuf -> increaseCapacity( fs.OutLen ); - } - - typename CFilterStep :: CResizePos* rpos = &(*fs.RPosBuf)[ PrevLen ]; - const int FracCount = fs.FltBank -> getFracCount(); - const double o = Vars.o; - const double k = Vars.k; - int i; - - for( i = PrevLen; i < fs.OutLen; i++ ) - { - const double SrcPos = o + k * i; - const int SrcPosInt = (int) floor( SrcPos ); - const double x = ( SrcPos - SrcPosInt ) * FracCount; - const int fti = (int) x; - rpos -> x = (typename fpclass :: fptypeatom) ( x - fti ); - rpos -> fti = fti; - rpos -> SrcPosInt = SrcPosInt; - rpos++; - } - } - - /** - * Function updates filtering step buffer lengths depending on the - * specified source and new scanline lengths. This function should be - * called after the buildFilterSteps() function. - * - * @param Steps Array that receives filtering steps. - * @param[out] Vars Variables object, will receive buffer size and length. - * This function expects "k" and "o" variable values that will be - * adjusted by this function. - * @param RPosBufArray Resizing position buffers array, used to obtain - * buffer to initialize and use (will be reused if it is already fully or - * partially filled). - * @param SrcLen Source scanline's length in pixels. - * @param NewLen New scanline's length in pixels. - */ - - static void updateFilterStepBuffers( CFilterSteps& Steps, - CImageResizerVars& Vars, - typename CFilterStep :: CRPosBufArray& RPosBufArray, int SrcLen, - const int NewLen ) - { - int upstep = -1; - int InBuf = 0; - int i; - - for( i = 0; i < Steps.getItemCount(); i++ ) - { - CFilterStep& fs = Steps[ i ]; - - fs.Vars = &Vars; - fs.InLen = SrcLen; - fs.InBuf = InBuf; - fs.OutBuf = ( InBuf + 1 ) & 1; - - if( fs.IsUpsample ) - { - upstep = i; - Vars.k *= fs.ResampleFactor; - Vars.o *= fs.ResampleFactor; - fs.InPrefix = 0; - fs.InSuffix = 0; - fs.OutLen = fs.InLen * fs.ResampleFactor; - fs.OutPrefix = fs.FltLatency; - fs.OutSuffix = fs.Flt.getCapacity() - fs.FltLatency - - fs.ResampleFactor; - - int l0 = fs.OutPrefix + fs.OutLen + fs.OutSuffix; - int l = fs.InLen * fs.ResampleFactor + - fs.SuffixDC.getCapacity(); - - if( l > l0 ) - { - fs.OutSuffix += l - l0; - } - - l0 = fs.OutLen + fs.OutSuffix; - - if( fs.PrefixDC.getCapacity() > l0 ) - { - fs.OutSuffix += fs.PrefixDC.getCapacity() - l0; - } - } - else - if( fs.ResampleFactor == 0 ) - { - const int FilterLenD2 = fs.FltBank -> getFilterLen() / 2; - const int FilterLenD21 = FilterLenD2 - 1; - - const int ResizeLPix = (int) floor( Vars.o ) - FilterLenD21; - fs.InPrefix = ( ResizeLPix < 0 ? -ResizeLPix : 0 ); - const int ResizeRPix = (int) floor( Vars.o + - ( NewLen - 1 ) * Vars.k ) + FilterLenD2 + 1; - - fs.InSuffix = ( ResizeRPix > fs.InLen ? - ResizeRPix - fs.InLen : 0 ); - - fs.OutLen = NewLen; - fs.RPosBuf = &RPosBufArray.getRPosBuf( Vars.k, Vars.o, - fs.FltBank -> getFracCount() ); - - fillRPosBuf( fs, Vars ); - } - else - { - Vars.k /= fs.ResampleFactor; - Vars.o /= fs.ResampleFactor; - Vars.o += fs.EdgePixelCount; - - fs.InPrefix = fs.FltLatency; - fs.InSuffix = fs.Flt.getCapacity() - fs.FltLatency - 1; - - // Additionally extend OutLen to produce more precise edge - // pixels. - - fs.OutLen = ( fs.InLen + fs.ResampleFactor - 1 ) / - fs.ResampleFactor + fs.EdgePixelCount; - - fs.InSuffix += ( fs.OutLen - 1 ) * fs.ResampleFactor + 1 - - fs.InLen; - - fs.InPrefix += fs.EdgePixelCount * fs.ResampleFactor; - fs.OutLen += fs.EdgePixelCount; - } - - InBuf = fs.OutBuf; - SrcLen = fs.OutLen; - } - - Steps[ Steps.getItemCount() - 1 ].OutBuf = 2; - Vars.IsResize2 = false; - - if( upstep != -1 ) - { - extendUpsample( Steps[ upstep ], Steps[ upstep + 1 ]); - - if( Steps[ upstep ].ResampleFactor == 2 && - Vars.ResizeStep == upstep + 1 && - fpclass :: packmode == 0 && - Steps[ upstep ].FltOrig.getCapacity() > 0 ) - { - // Interpolation with preceeding 2x filterless upsample, - // interleaved resizing only. - - Vars.IsResize2 = true; - } - } - } - - /** - * Function calculates an optimal intermediate buffer length that will - * cover all needs of the specified filtering steps. This function should - * be called after the updateFilterStepBuffers() function. - * - * Function also updates resizing step's RPosBuf pointers to the filter - * bank and SrcOffs values. - * - * @param Steps Filtering steps. - * @param[out] Vars Variables object, will receive buffer size and length. - * @param ResElIncr Resulting (final) element increment, used to produce - * de-interleaved result. For horizontal processing this value is equal - * to last step's OutLen, for vertical processing this value is equal to - * resulting image's width. - */ - - static void updateBufLenAndRPosPtrs( CFilterSteps& Steps, - CImageResizerVars& Vars, const int ResElIncr ) - { - int MaxPrefix[ 2 ] = { 0, 0 }; - int MaxLen[ 2 ] = { 0, 0 }; - int i; - - for( i = 0; i < Steps.getItemCount(); i++ ) - { - CFilterStep& fs = Steps[ i ]; - const int ib = fs.InBuf; - - if( fs.InPrefix > MaxPrefix[ ib ]) - { - MaxPrefix[ ib ] = fs.InPrefix; - } - - int l = fs.InLen + fs.InSuffix; - - if( l > MaxLen[ ib ]) - { - MaxLen[ ib ] = l; - } - - fs.InElIncr = fs.InPrefix + l; - - if( fs.OutBuf == 2 ) - { - break; - } - - const int ob = fs.OutBuf; - - if( fs.IsUpsample ) - { - if( fs.OutPrefix > MaxPrefix[ ob ]) - { - MaxPrefix[ ob ] = fs.OutPrefix; - } - - l = fs.OutLen + fs.OutSuffix; - - if( l > MaxLen[ ob ]) - { - MaxLen[ ob ] = l; - } - } - else - { - if( fs.OutLen > MaxLen[ ob ]) - { - MaxLen[ ob ] = fs.OutLen; - } - } - } - - // Update OutElIncr values of all steps. - - for( i = 0; i < Steps.getItemCount(); i++ ) - { - CFilterStep& fs = Steps[ i ]; - - if( fs.OutBuf == 2 ) - { - fs.OutElIncr = ResElIncr; - break; - } - - CFilterStep& fs2 = Steps[ i + 1 ]; - - if( fs.IsUpsample ) - { - fs.OutElIncr = fs.OutPrefix + fs.OutLen + fs.OutSuffix; - - if( fs.OutElIncr > fs2.InElIncr ) - { - fs2.InElIncr = fs.OutElIncr; - } - else - { - fs.OutElIncr = fs2.InElIncr; - } - } - else - { - fs.OutElIncr = fs2.InElIncr; - } - } - - // Update temporary buffer's length. - - for( i = 0; i < 2; i++ ) - { - Vars.BufLen[ i ] = MaxPrefix[ i ] + MaxLen[ i ]; - Vars.BufOffs[ i ] = MaxPrefix[ i ]; - - if( Vars.packmode == 0 ) - { - Vars.BufOffs[ i ] *= Vars.ElCount; - } - - Vars.BufLen[ i ] *= Vars.ElCount; - } - - // Update RPosBuf pointers and SrcOffs. - - CFilterStep& fs = Steps[ Vars.ResizeStep ]; - typename CFilterStep :: CResizePos* rpos = &(*fs.RPosBuf)[ 0 ]; - const int em = ( fpclass :: packmode == 0 ? Vars.ElCount : 1 ); - const int fl = fs.FltBank -> getFilterLen(); - const int FilterLenD21 = fl / 2 - 1; - - if( Vars.IsResize2 ) - { - for( i = 0; i < fs.OutLen; i++ ) - { - const int p = rpos -> SrcPosInt - FilterLenD21; - const int fo = p & 1; - rpos -> SrcOffs = ( p + fo ) * em; - rpos -> ftp = fs.FltBank -> getFilter( rpos -> fti ) + fo; - rpos -> fl = fl - fo; - rpos++; - } - } - else - { - for( i = 0; i < fs.OutLen; i++ ) - { - rpos -> SrcOffs = ( rpos -> SrcPosInt - FilterLenD21 ) * em; - rpos -> ftp = fs.FltBank -> getFilter( rpos -> fti ); - rpos++; - } - } - } - - /** - * Function modifies the overall (DC) gain of the correction filter in the - * pre-built filtering steps array. - * - * @param Steps Filtering steps. - * @param m Multiplier to apply to the correction filter. - */ - - void modifyCorrFilterDCGain( CFilterSteps& Steps, const double m ) const - { - CBuffer< fptype >* Flt; - const int z = Steps.getItemCount() - 1; - - if( !Steps[ z ].IsUpsample && Steps[ z ].ResampleFactor == 1 ) - { - Flt = &Steps[ z ].Flt; - } - else - { - Flt = &Steps[ 0 ].Flt; - } - - int i; - - for( i = 0; i < Flt -> getCapacity(); i++ ) - { - (*Flt)[ i ] = (fptype) ( (double) (*Flt)[ i ] * m ); - } - } - - /** - * Function builds a map of used fractional delay filters based on the - * resizing positions buffer. - * - * @param fs Resizing step. - * @param[out] UsedFracMap Map of used fractional delay filters. - */ - - static void fillUsedFracMap( const CFilterStep& fs, - CBuffer< uint8_t >& UsedFracMap ) - { - const int FracCount = fs.FltBank -> getFracCount(); - UsedFracMap.increaseCapacity( FracCount, false ); - memset( &UsedFracMap[ 0 ], 0, FracCount * sizeof( UsedFracMap[ 0 ])); - - typename CFilterStep :: CResizePos* rpos = &(*fs.RPosBuf)[ 0 ]; - int i; - - for( i = 0; i < fs.OutLen; i++ ) - { - UsedFracMap[ rpos -> fti ] |= 1; - rpos++; - } - } - - /** - * Function calculates the overall filtering steps complexity per - * scanline. Each complexity unit corresponds to a single multiply-add - * operation. Data copy and pointer math operations are not included in - * this calculation, it is assumed that they correlate to the multiply-add - * operations. Calculation also does not include final rounding, dithering - * and clamping operations since they cannot be optimized out anyway. - * - * Calculation of the CRPosBuf buffer is not included since it cannot be - * avoided. - * - * This function should be called after the updateFilterStepBuffers() - * function. - * - * @param Steps Filtering steps array. - * @param Vars Variables object. - * @param UsedFracMap The map of used fractional delay filters. - * @param ScanlineCount Scanline count. - */ - - static int calcComplexity( const CFilterSteps& Steps, - const CImageResizerVars& Vars, const CBuffer< uint8_t >& UsedFracMap, - const int ScanlineCount ) - { - int fcnum; // Filter complexity multiplier numerator. - int fcdenom; // Filter complexity multiplier denominator. - - if( Vars.packmode != 0 ) - { - fcnum = 1; - fcdenom = 1; - } - else - { - // In interleaved processing mode, filters require 1 less - // multiplication per 2 multiply-add instructions. - - fcnum = 3; - fcdenom = 4; - } - - int s = 0; // Complexity per one scanline. - int s2 = 0; // Complexity per all scanlines. - int i; - - for( i = 0; i < Steps.getItemCount(); i++ ) - { - const CFilterStep& fs = Steps[ i ]; - - s2 += 65 * fs.Flt.getCapacity(); // Filter creation complexity. - - if( fs.IsUpsample ) - { - if( fs.FltOrig.getCapacity() > 0 ) - { - continue; - } - - s += ( fs.Flt.getCapacity() * - ( fs.InPrefix + fs.InLen + fs.InSuffix ) + - fs.SuffixDC.getCapacity() + fs.PrefixDC.getCapacity() ) * - Vars.ElCount; - } - else - if( fs.ResampleFactor == 0 ) - { - s += fs.FltBank -> getFilterLen() * - ( fs.FltBank -> getOrder() + Vars.ElCount ) * fs.OutLen; - - if( i == Vars.ResizeStep && Vars.IsResize2 ) - { - s >>= 1; - } - - s2 += fs.FltBank -> calcInitComplexity( UsedFracMap ); - } - else - { - s += fs.Flt.getCapacity() * Vars.ElCount * fs.OutLen * - fcnum / fcdenom; - } - } - - return( s + s2 / ScanlineCount ); - } - - /** - * @brief Thread-isolated data used for scanline processing. - * - * This structure holds data necessary for image's horizontal or vertical - * scanline processing, including scanline processing queue. - * - * @tparam Tin Source element data type. Intermediate buffers store data - * in floating point format. - * @tparam Tout Destination element data type. Intermediate buffers store - * data in floating point format. - */ - - template< class Tin, class Tout > - class CThreadData : public CImageResizerThreadPool :: CWorkload - { - public: - virtual void process() - { - processScanlineQueue(); - } - - /** - * This enumeration lists possible scanline operations. - */ - - enum EScanlineOperation - { - sopResizeH, ///< Resize horizontal scanline. - ///< - sopResizeV, ///< Resize vertical scanline. - ///< - sopDitherAndUnpackH, ///< Dither and unpack horizontal scanline. - ///< - sopUnpackH ///< Unpack horizontal scanline. - ///< - }; - - /** - * Function initializes *this thread data object and assigns certain - * variables provided by the higher level code. - * - * @param aThreadIndex Index of this thread data (0-based). - * @param aThreadCount Total number of threads used during processing. - * @param aSteps Filtering steps. - * @param aVars Image resizer variables. - */ - - void init( const int aThreadIndex, const int aThreadCount, - const CFilterSteps& aSteps, const CImageResizerVars& aVars ) - { - ThreadIndex = aThreadIndex; - ThreadCount = aThreadCount; - Steps = &aSteps; - Vars = &aVars; - } - - /** - * Function initializes scanline processing queue, and updates - * capacities of intermediate buffers. - * - * @param aOp Operation to perform over scanline. - * @param TotalLines The total number of scanlines that will be - * processed by all threads. - * @param aSrcLen Source scanline length in pixels. - * @param aSrcIncr Source scanline buffer increment. Ignored in - * horizontal scanline processing. - * @param aResIncr Resulting scanline buffer increment. Ignored in - * horizontal scanline processing. - */ - - void initScanlineQueue( const EScanlineOperation aOp, - const int TotalLines, const int aSrcLen, const int aSrcIncr = 0, - const int aResIncr = 0 ) - { - const int l = Vars -> BufLen[ 0 ] + Vars -> BufLen[ 1 ]; - - if( Bufs.getCapacity() < l ) - { - Bufs.alloc( l, fpclass :: fpalign ); - } - - BufPtrs[ 0 ] = Bufs + Vars -> BufOffs[ 0 ]; - BufPtrs[ 1 ] = Bufs + Vars -> BufLen[ 0 ] + Vars -> BufOffs[ 1 ]; - - int j; - int ml = 0; - - for( j = 0; j < Steps -> getItemCount(); j++ ) - { - const CFilterStep& fs = (*Steps)[ j ]; - - if( fs.ResampleFactor == 0 && - ml < fs.FltBank -> getFilterLen() ) - { - ml = fs.FltBank -> getFilterLen(); - } - } - - TmpFltBuf.alloc( ml, fpclass :: fpalign ); - ScanlineOp = aOp; - SrcLen = aSrcLen; - SrcIncr = aSrcIncr; - ResIncr = aResIncr; - QueueLen = 0; - Queue.increaseCapacity(( TotalLines + ThreadCount - 1 ) / - ThreadCount, false ); - } - - /** - * Function adds a scanline to the queue buffer. The - * initScanlineQueue() function should be called before calling this - * function. The number of calls to this add function should not - * exceed the TotalLines spread over all threads. - * - * @param SrcBuf Source scanline buffer. - * @param ResBuf Resulting scanline buffer. - */ - - void addScanlineToQueue( void* const SrcBuf, void* const ResBuf ) - { - Queue[ QueueLen ].SrcBuf = SrcBuf; - Queue[ QueueLen ].ResBuf = ResBuf; - QueueLen++; - } - - /** - * Function processes all queued scanlines. - */ - - void processScanlineQueue() - { - int i; - - switch( ScanlineOp ) - { - case sopResizeH: - { - for( i = 0; i < QueueLen; i++ ) - { - resizeScanlineH( (Tin*) Queue[ i ].SrcBuf, - (fptype*) Queue[ i ].ResBuf ); - } - - break; - } - - case sopResizeV: - { - for( i = 0; i < QueueLen; i++ ) - { - resizeScanlineV( (fptype*) Queue[ i ].SrcBuf, - (fptype*) Queue[ i ].ResBuf ); - } - - break; - } - - case sopDitherAndUnpackH: - { - if( Vars -> UseSRGBGamma ) - { - for( i = 0; i < QueueLen; i++ ) - { - CFilterStep :: applySRGBGamma( - (fptype*) Queue[ i ].SrcBuf, SrcLen, *Vars ); - - Ditherer.dither( (fptype*) Queue[ i ].SrcBuf ); - - CFilterStep :: unpackScanline( - (fptype*) Queue[ i ].SrcBuf, - (Tout*) Queue[ i ].ResBuf, SrcLen, *Vars ); - } - } - else - { - for( i = 0; i < QueueLen; i++ ) - { - Ditherer.dither( (fptype*) Queue[ i ].SrcBuf ); - - CFilterStep :: unpackScanline( - (fptype*) Queue[ i ].SrcBuf, - (Tout*) Queue[ i ].ResBuf, SrcLen, *Vars ); - } - } - - break; - } - - case sopUnpackH: - { - if( Vars -> UseSRGBGamma ) - { - for( i = 0; i < QueueLen; i++ ) - { - CFilterStep :: applySRGBGamma( - (fptype*) Queue[ i ].SrcBuf, SrcLen, *Vars ); - - CFilterStep :: unpackScanline( - (fptype*) Queue[ i ].SrcBuf, - (Tout*) Queue[ i ].ResBuf, SrcLen, *Vars ); - } - } - else - { - for( i = 0; i < QueueLen; i++ ) - { - CFilterStep :: unpackScanline( - (fptype*) Queue[ i ].SrcBuf, - (Tout*) Queue[ i ].ResBuf, SrcLen, *Vars ); - } - } - - break; - } - } - } - - /** - * Function returns ditherer object associated with *this thread data - * object. - */ - - CDitherer& getDitherer() - { - return( Ditherer ); - } - - private: - int ThreadIndex; ///< Thread index. - ///< - int ThreadCount; ///< Thread count. - ///< - const CFilterSteps* Steps; ///< Filtering steps. - ///< - const CImageResizerVars* Vars; ///< Image resizer variables. - ///< - CBuffer< fptype > Bufs; ///< Flip-flop intermediate buffers. - ///< - fptype* BufPtrs[ 3 ]; ///< Flip-flop buffer pointers (referenced by - ///< filtering step's InBuf and OutBuf indices). - ///< - CBuffer< fptype > TmpFltBuf; ///< Temporary buffer used in the - ///< doResize() function, aligned by fpclass :: fpalign. - ///< - EScanlineOperation ScanlineOp; ///< Operation to perform over - ///< scanline. - ///< - int SrcLen; ///< Source scanline length in the last queue. - ///< - int SrcIncr; ///< Source scanline buffer increment in the last queue. - ///< - int ResIncr; ///< Resulting scanline buffer increment in the last - ///< queue. - ///< - CDitherer Ditherer; ///< Ditherer object to use. - ///< - - /** - * @brief Scanline processing queue item. - * - * Scanline processing queue item. - */ - - struct CQueueItem - { - void* SrcBuf; ///< Source scanline buffer, will by typecasted to - ///< Tin or fptype*. - ///< - void* ResBuf; ///< Resulting scanline buffer, will by typecasted - ///< to Tout or fptype*. - ///< - }; - - CBuffer< CQueueItem > Queue; ///< Scanline processing queue. - ///< - int QueueLen; ///< Queue length. - ///< - - /** - * Function resizes a single horizontal scanline. - * - * @param SrcBuf Source scanline buffer. Can be either horizontal or - * vertical. - * @param ResBuf Resulting scanline buffer. - */ - - void resizeScanlineH( const Tin* const SrcBuf, fptype* const ResBuf ) - { - const CFilterStep& fs0 = (*Steps)[ 0 ]; - - fs0.packScanline( SrcBuf, BufPtrs[ 0 ], SrcLen ); - BufPtrs[ 2 ] = ResBuf; - - fptype ElBiases[ 4 ]; - fs0.calcScanlineBias( BufPtrs[ 0 ], SrcLen, ElBiases ); - fs0.unbiasScanline( BufPtrs[ 0 ], SrcLen, ElBiases ); - - int j; - - for( j = 0; j < Steps -> getItemCount(); j++ ) - { - const CFilterStep& fs = (*Steps)[ j ]; - fs.prepareInBuf( BufPtrs[ fs.InBuf ]); - const int DstIncr = - ( Vars -> packmode == 0 ? Vars -> ElCount : 1 ); - - if( fs.ResampleFactor != 0 ) - { - if( fs.IsUpsample ) - { - fs.doUpsample( BufPtrs[ fs.InBuf ], - BufPtrs[ fs.OutBuf ]); - } - else - { - fs.doFilter( BufPtrs[ fs.InBuf ], - BufPtrs[ fs.OutBuf ], DstIncr ); - } - } - else - { - if( Vars -> IsResize2 ) - { - fs.doResize2( BufPtrs[ fs.InBuf ], - BufPtrs[ fs.OutBuf ], DstIncr, ElBiases, - TmpFltBuf ); - } - else - { - fs.doResize( BufPtrs[ fs.InBuf ], - BufPtrs[ fs.OutBuf ], DstIncr, ElBiases, - TmpFltBuf ); - } - } - } - } - - /** - * Function resizes a single vertical scanline. - * - * @param SrcBuf Source scanline buffer. Can be either horizontal or - * vertical. - * @param ResBuf Resulting scanline buffer. - */ - - void resizeScanlineV( const fptype* const SrcBuf, - fptype* const ResBuf ) - { - const CFilterStep& fs0 = (*Steps)[ 0 ]; - - fs0.convertVtoH( SrcBuf, BufPtrs[ 0 ], SrcLen, SrcIncr ); - BufPtrs[ 2 ] = ResBuf; - - fptype ElBiases[ 4 ]; - fs0.calcScanlineBias( BufPtrs[ 0 ], SrcLen, ElBiases ); - fs0.unbiasScanline( BufPtrs[ 0 ], SrcLen, ElBiases ); - - int j; - - for( j = 0; j < Steps -> getItemCount(); j++ ) - { - const CFilterStep& fs = (*Steps)[ j ]; - fs.prepareInBuf( BufPtrs[ fs.InBuf ]); - const int DstIncr = ( fs.OutBuf == 2 ? ResIncr : - ( Vars -> packmode == 0 ? Vars -> ElCount : 1 )); - - if( fs.ResampleFactor != 0 ) - { - if( fs.IsUpsample ) - { - fs.doUpsample( BufPtrs[ fs.InBuf ], - BufPtrs[ fs.OutBuf ]); - } - else - { - fs.doFilter( BufPtrs[ fs.InBuf ], - BufPtrs[ fs.OutBuf ], DstIncr ); - } - } - else - { - if( Vars -> IsResize2 ) - { - fs.doResize2( BufPtrs[ fs.InBuf ], - BufPtrs[ fs.OutBuf ], DstIncr, ElBiases, - TmpFltBuf ); - } - else - { - fs.doResize( BufPtrs[ fs.InBuf ], - BufPtrs[ fs.OutBuf ], DstIncr, ElBiases, - TmpFltBuf ); - } - } - } - } - }; -}; - -#undef AVIR_PI -#undef AVIR_PId2 -#undef AVIR_NOCTOR - -} // namespace avir - -#endif // AVIR_CIMAGERESIZER_INCLUDED diff --git a/libraries/Camera/examples/GigaCameraDisplay/display.cpp b/libraries/Camera/examples/GigaCameraDisplay/display.cpp deleted file mode 100644 index 4d4b6d4e4..000000000 --- a/libraries/Camera/examples/GigaCameraDisplay/display.cpp +++ /dev/null @@ -1,351 +0,0 @@ -//#include "Types.h" -#include "Arduino.h" - - -/* Command2 BKx selection command */ -#define DSI_CMD2BKX_SEL 0xFF -#define DSI_CMD2BK1_SEL 0x11 -#define DSI_CMD2BK0_SEL 0x10 -#define DSI_CMD2BKX_SEL_NONE 0x00 - -/* Command2, BK0 commands */ -#define DSI_CMD2_BK0_PVGAMCTRL 0xB0 /* Positive Voltage Gamma Control */ -#define DSI_CMD2_BK0_NVGAMCTRL 0xB1 /* Negative Voltage Gamma Control */ -#define DSI_CMD2_BK0_LNESET 0xC0 /* Display Line setting */ -#define DSI_CMD2_BK0_PORCTRL 0xC1 /* Porch control */ -#define DSI_CMD2_BK0_INVSEL 0xC2 /* Inversion selection, Frame Rate Control */ - -/* Command2, BK1 commands */ -#define DSI_CMD2_BK1_VRHS 0xB0 /* Vop amplitude setting */ -#define DSI_CMD2_BK1_VCOM 0xB1 /* VCOM amplitude setting */ -#define DSI_CMD2_BK1_VGHSS 0xB2 /* VGH Voltage setting */ -#define DSI_CMD2_BK1_TESTCMD 0xB3 /* TEST Command Setting */ -#define DSI_CMD2_BK1_VGLS 0xB5 /* VGL Voltage setting */ -#define DSI_CMD2_BK1_PWCTLR1 0xB7 /* Power Control 1 */ -#define DSI_CMD2_BK1_PWCTLR2 0xB8 /* Power Control 2 */ -#define DSI_CMD2_BK1_SPD1 0xC1 /* Source pre_drive timing set1 */ -#define DSI_CMD2_BK1_SPD2 0xC2 /* Source EQ2 Setting */ -#define DSI_CMD2_BK1_MIPISET1 0xD0 /* MIPI Setting 1 */ - -#define MIPI_DCS_SOFT_RESET 0x01 -#define MIPI_DCS_EXIT_SLEEP_MODE 0x11 - -#ifndef U16 -#define U16 uint16_t -#endif - -#define SSD_MODE(a,b) -#define Set_POWER(a,b,c,d) -#define Set_STANDBY() -#define Set_BOOST(a,b,c,d) -#define Set_RESET(a,b) -#define SSD_LANE(a,b) - -extern DSI_HandleTypeDef dsi; -#define hdsi_eval dsi - -#define LCD_ST7701_ID 0x00 // VC (Virtual channel, for using muliple displays) - -#define Delay(x) delay(x) - -uint8_t BUFFER[8]; -//////////////////////////////////////////////////////// -// Procedure: DCS_Short_Write_NP -/// Description: -/// @param None -/// @retval None -/// @details -/// -//////////////////////////////////////////////////////// -void DCS_Short_Write_NP(uint8_t data0) -{ -// HAL_DSI_ShortWrite(&hdsi_eval, LCD_ST7701_ID, DSI_DCS_SHORT_PKT_WRITE_P0, data0, 0x00); // DSI_DCS_SHORT_PKT_WRITE_P0 - Serial.print("before DCS_Short_Write_NP "); - Serial.println(millis()); - HAL_DSI_ShortWrite(&hdsi_eval, LCD_ST7701_ID, DSI_DCS_SHORT_PKT_WRITE_P1, data0, 0x00); // DSI_DCS_SHORT_PKT_WRITE_P0 - Serial.print("after "); - Serial.println(millis()); -} - -//////////////////////////////////////////////////////// -// Procedure: -/// Description: -/// @param None -/// @retval None -/// @details -/// -//////////////////////////////////////////////////////// -void Generic_Short_Write_1P (uint8_t data0, uint8_t data1) -{ -Serial.print("before Generic_Short_Write_1P "); - Serial.println(millis()); - HAL_DSI_ShortWrite(&hdsi_eval, LCD_ST7701_ID, DSI_GEN_SHORT_PKT_WRITE_P1, data0, data1); -Serial.print("after "); - Serial.println(millis()); -} - - -//////////////////////////////////////////////////////// -// Procedure: -/// Description: -/// @param None -/// @retval None -/// @details -/// -//////////////////////////////////////////////////////// -void Generic_Long_Write(uint8_t* pdata, int length) -{ - uint8_t data_buf[19] = {0}; // data_buf size with command(*pdata) = 20 so data is alligned 4 - uint8_t data_length = length- 1; - - if (data_length > sizeof(data_buf)) - return; - - memcpy(data_buf, pdata+1, data_length); -// HAL_DSI_LongWrite(&hdsi_eval, LCD_ST7701_ID, DSI_GEN_LONG_PKT_WRITE, data_length, *pdata, data_buf); -Serial.print("before Generic_Long_Write "); - Serial.println(millis()); - HAL_DSI_LongWrite(&hdsi_eval, LCD_ST7701_ID, DSI_DCS_LONG_PKT_WRITE, data_length, *pdata, data_buf); -Serial.print("after "); - Serial.println(millis()); -} -//////////////////////////////////////////////////////// -// Procedure: DCS_Short_Read_NP -/// Description: -/// @param None -/// @retval None -/// @details -/// -//////////////////////////////////////////////////////// -void DCS_Short_Read_NP(uint8_t data0, int length, uint8_t* p_data) -{ - HAL_DSI_Read(&hdsi_eval, LCD_ST7701_ID, p_data, length, DSI_DCS_SHORT_PKT_READ, data0, NULL); -} - -//------------------------------------------------------------------------- -- -// Example: -// Model - HSD040B8W9 -// IC - ST7701 -// Width - 480 -// Height - 800 -// Author - Huangyu(Bitland) -// History: -// V01 2015/05/18 -// -// Disclaimer: -// This C source code is intended as a design reference -// which illustrates how these types of functions can be implemented. -// It is the user's responsibility to verify their design for -// consistency and functionality through the use of formal -// verification methods. LCD Studio provides no warranty regarding the use -// or functionality of this code. -//-------------------------------------------------------------------------------------- -const U16 _E5[17] = {0xE5,0x0E,0x2D,0xA0,0xa0,0x10,0x2D,0xA0,0xA0,0x0A,0x2D,0xA0,0xA0,0x0C,0x2D,0xA0,0xA0}; -const U16 _E8[17] = {0xE8,0x0D,0x2D,0xA0,0xA0,0x0F,0x2D,0xA0,0xA0,0x09,0x2D,0xA0,0xA0,0x0B,0x2D,0xA0,0xA0}; -const U16 _ED[17] = {0xED,0xAB,0x89,0x76,0x54,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x10,0x45,0x67,0x98,0xBA}; - - -/**************************************************/ -// I2C power module controller -/**************************************************/ - -void DisplayOn() -{ - Set_POWER(1,1,1,1);//1.8V ON, 2.8V ON, 5V ON, BL ON - -} - - -// -void PowerOffSequence() -{ - DCS_Short_Write_NP(0x28); - Delay(200); - DCS_Short_Write_NP(0x10); - Delay(100); - Set_STANDBY();//Video transfer stop - Delay(50); - - Set_RESET(1,0);//MIPI RESET 1, LCD RESET 0 - Delay(50); - Set_RESET(0,0);//MIPI RESET 0, LCD RESET 0 - Delay(50); - - Set_POWER(1,1,0,1);//1.8V ON, 2.8V ON, 5V OFF, BL ON - Delay(50); - - Set_BOOST(5.00, 5.00, 0x81, 50);//VDD, VEE, OFF:VDD->VEE, 10ms - Delay(50); - - Set_POWER(1,0,0,1);//1.8V ON, 2.8V OFF, 5V OFF, BL ON - Delay(100); - Set_POWER(0,0,0,0);//1.8V OFF, 2.8V OFF, 5V OFF, BL OFF - -} - -/**************************************************/ -// Read function (Option) -/**************************************************/ -// -void ReadOperation() -{ - //Clean memory: BUFFER - memset(BUFFER, 0, sizeof(BUFFER));//BUFFER size: 8 Bytes - - //Read value to BUFFER - DCS_Short_Read_NP(0xDA, 1, BUFFER+0); - DCS_Short_Read_NP(0xDB, 1, BUFFER+1); - DCS_Short_Read_NP(0xDC, 1,BUFFER+2); -} -volatile int LENGTH; -#define ST7701_DSI(mipi_dsi, seq...) \ -do { \ - const u8 d[] = { seq }; \ - LENGTH = ARRAY_SIZE(d); \ -}while(0) - - -void LCD_ST7701_Init(void) -{ - pinMode(PD_3, OUTPUT); - pinMode(PD_3, HIGH); - delay(200); - pinMode(PD_3, LOW); - delay(200); - pinMode(PD_3, HIGH); - - DCS_Short_Write_NP(MIPI_DCS_SOFT_RESET); - Delay(200); - - Serial.println("here"); - - //ST7701S+IVO5.0 - DCS_Short_Write_NP(MIPI_DCS_EXIT_SLEEP_MODE); - - // tesrt - - //------------------------------------------Bank0 Setting----------------------------------------------------// - //------------------------------------Display Control setting----------------------------------------------// - Delay(800); - - const uint8_t Display_Control_0[] = {0xFF,0x77,0x01,0x00,0x00,0x10}; //Generic_Long_Write_5P - const uint8_t Display_Control_1[] = {0xC0,0x63,0x00}; //Generic_Long_Write_2P - const uint8_t Display_Control_2[] = {0xC1,0x11,0x02}; - const uint8_t Display_Control_3[] = {0xC2,0x01,0x08}; - const uint8_t Display_Control_4[] = {0xCC,0x18}; - // const uint8_t Display_Control_5[] = {0x3a,0x50}; // color mode 565:50h 666:60h 888:70h - - Generic_Long_Write((uint8_t*)Display_Control_0, sizeof(Display_Control_0)); - Generic_Long_Write((uint8_t*)Display_Control_1, sizeof(Display_Control_1)); - Generic_Long_Write((uint8_t*)Display_Control_2, sizeof(Display_Control_2)); - Generic_Long_Write((uint8_t*)Display_Control_3, sizeof(Display_Control_3)); - Generic_Long_Write((uint8_t*)Display_Control_4, sizeof(Display_Control_4)); - - //-------------------------------------Gamma Cluster Setting-------------------------------------------// -// Ver1 const uint8_t _B0[17] = {0xB0,0x00, 0x0E, 0x15, 0x0F, 0x11, 0x08, 0x08, 0x08, 0x08, 0x23, 0x04, 0x13, 0x12,0x2B, 0x34, 0x1F}; - const uint8_t _B0[] = {0xB0,0x40, 0xc9, 0x91, 0x0d, - 0x12, 0x07, 0x02, 0x09, 0x09, 0x1f, 0x04, 0x50, 0x0f, - 0xe4, 0x29, 0xdf}; - -// Ver1 const uint8_t _B1[17] = {0xB1, 0x00, 0x0E, 0x95, 0x0F,0x13, 0x07, 0x09, 0x08, 0x08, 0x22, 0x04, 0x10, 0x0E,0x2C, 0x34, 0x1F}; - const uint8_t _B1[] = {0xB1, 0x40, 0xcb, 0xd0, 0x11, - 0x92, 0x07, 0x00, 0x08, 0x07, 0x1c, 0x06, 0x53, 0x12, - 0x63, 0xeb, 0xdf}; - - Generic_Long_Write((uint8_t*)_B0, sizeof(_B0)); - Generic_Long_Write((uint8_t*)_B1, sizeof(_B1)); - //---------------------------------------End Gamma Setting----------------------------------------------// - //------------------------------------End Display Control setting----------------------------------------// - //-----------------------------------------Bank0 Setting End---------------------------------------------// - //-------------------------------------------Bank1 Setting---------------------------------------------------// - //-------------------------------- Power Control Registers Initial --------------------------------------// - const uint8_t _FF1[] = {DSI_CMD2BKX_SEL,0x77,0x01,0x00,0x00,DSI_CMD2BK1_SEL}; - Generic_Long_Write ((uint8_t*)_FF1, sizeof(_FF1)); - - -// Generic_Short_Write_1P (DSI_CMD2_BK1_VRHS,0x50); - Generic_Short_Write_1P (DSI_CMD2_BK1_VRHS,0x65); - //-------------------------------------------Vcom Setting---------------------------------------------------// -// Generic_Short_Write_1P (DSI_CMD2_BK1_VCOM,0x68); - Generic_Short_Write_1P (DSI_CMD2_BK1_VCOM,0x34); - //-----------------------------------------End Vcom Setting-----------------------------------------------// -// Generic_Short_Write_1P (DSI_CMD2_BK1_VGHSS,0x07); - Generic_Short_Write_1P (DSI_CMD2_BK1_VGHSS,0x87); - - Generic_Short_Write_1P (DSI_CMD2_BK1_TESTCMD,0x80); -// Generic_Short_Write_1P (DSI_CMD2_BK1_VGLS,0x47); - Generic_Short_Write_1P (DSI_CMD2_BK1_VGLS,0x49); - Generic_Short_Write_1P (DSI_CMD2_BK1_PWCTLR1,0x85); -// Generic_Short_Write_1P (DSI_CMD2_BK1_PWCTLR2,0x21); - Generic_Short_Write_1P (DSI_CMD2_BK1_PWCTLR2,0x20); - Generic_Short_Write_1P (0xB9,0x10); - Generic_Short_Write_1P (DSI_CMD2_BK1_SPD1,0x78); - Generic_Short_Write_1P (DSI_CMD2_BK1_SPD2,0x78); - Generic_Short_Write_1P (DSI_CMD2_BK1_MIPISET1,0x88); - //---------------------------------End Power Control Registers Initial -------------------------------// - Delay(100); - //---------------------------------------------GIP Setting----------------------------------------------------// - const uint8_t _E0[] = {0xE0,0x00,0x00,0x02}; - Generic_Long_Write((uint8_t*)_E0, sizeof(_E0)); - //----------------------------------GIP---------------------------------------------------- - const uint8_t _E1[] = {0xE1,0x08,0x00,0x0A,0x00,0x07,0x00,0x09,0x00,0x00,0x33,0x33}; - const uint8_t _E2[] = {0xE2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - Generic_Long_Write((uint8_t*)_E1, sizeof(_E1)); - Generic_Long_Write((uint8_t*)_E2, sizeof(_E2)); - - //-------------------------------------------------------------------------------------- - const uint8_t _E3[] = {0xE3,0x00,0x00,0x33,0x33}; - const uint8_t _E4[] = {0xE4,0x44,0x44}; - Generic_Long_Write((uint8_t*)_E3, sizeof(_E3)); - Generic_Long_Write((uint8_t*)_E4, sizeof(_E4)); - -// const uint8_t _E5[] = {0xE5,0x0E,0x2D,0xA0,0xa0,0x10,0x2D,0xA0,0xA0,0x0A,0x2D,0xA0,0xA0,0x0C,0x2D,0xA0,0xA0}; - const uint8_t _E5[] = {0xE5,0x0E,0x60,0xA0,0xa0,0x10,0x60,0xA0,0xA0,0x0A,0x60,0xA0,0xA0,0x0C,0x60,0xA0,0xA0}; - Generic_Long_Write((uint8_t*)_E5, sizeof(_E5)); - - const uint8_t _E6[] = {0xE6,0x00,0x00,0x33,0x33}; - const uint8_t _E7[] = {0xE7,0x44,0x44}; - Generic_Long_Write((uint8_t*)_E6, sizeof(_E6)); - Generic_Long_Write((uint8_t*)_E7, sizeof(_E7)); - -// const uint8_t _E8[] = {0xE8,0x0D,0x2D,0xA0,0xA0,0x0F,0x2D,0xA0,0xA0,0x09,0x2D,0xA0,0xA0,0x0B,0x2D,0xA0,0xA0}; - const uint8_t _E8[] = {0xE8,0x0D,0x60,0xA0,0xA0,0x0F,0x60,0xA0,0xA0,0x09,0x60,0xA0,0xA0,0x0B,0x60,0xA0,0xA0}; - Generic_Long_Write((uint8_t*)_E8, sizeof(_E8)); - - const uint8_t _EB[] = {0xEB,0x02,0x01,0xE4,0xE4,0x44,0x00,0x40}; - const uint8_t _EC[] = {0xEC,0x02,0x01}; - Generic_Long_Write((uint8_t*)_EB, sizeof(_EB)); - Generic_Long_Write((uint8_t*)_EC, sizeof(_EC)); - - const uint8_t _ED[17] = {0xED,0xAB,0x89,0x76,0x54,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x10,0x45,0x67,0x98,0xBA}; - Generic_Long_Write((uint8_t*)_ED, sizeof(_ED)); - - //--------------------------------------------End GIP Setting-----------------------------------------------// - //------------------------------ Power Control Registers Initial End-----------------------------------// - //------------------------------------------Bank1 Setting----------------------------------------------------// - const uint8_t _FF2[] = {DSI_CMD2BKX_SEL,0x77,0x01,0x00,0x00,DSI_CMD2BKX_SEL_NONE}; - Generic_Long_Write ((uint8_t*)_FF2, sizeof(_FF2)); - - Delay(10); - DCS_Short_Write_NP(0x29); - Delay(200); - - - SSD_MODE(0,1); - -// ReadOperation(); - /* - Read_ADC(2.80,1,0.2);//��ȡӲ��ID ��ѹ��Χ0V - 3.3V - - if(!memcmp("0x01")) //�жϵ�ѹ���ڵķ�Χ�� - { - Set_TEXT(0, 1, 0x22);//��Ļ��ʾ��ɫ������������ - } - else - { - Set_TEXT(0, 0, 0x01);//Draw Text: "PASS" - - } - - */ -}