<commit_before>/*
 *
 *    Copyright (c) 2020 Project CHIP Authors
 *    Copyright (c) 2018 Nest Labs, Inc.
 *    All rights reserved.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 * @file Display.cpp
 *
 * This file implements helper APIs for the M5Stack's display
 *
 */

#include <string.h>

#include "driver/ledc.h"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"

#include "Display.h"

#if CONFIG_HAVE_DISPLAY

// Brightness picked such that it's easy for cameras to focus on
#define DEFFAULT_BRIGHTNESS_PERCENT 10

// 8MHz is the recommended SPI speed to init the driver with
// It later gets set to the preconfigured defaults within the driver
#define TFT_SPI_CLOCK_INIT_HZ 8000000

// The frequency used by the ledc timer
// value chosen to eliminate flicker
#define LEDC_PWM_HZ 1000

// with a duty resolution of LEDC_TIMER_8_BIT
// the highest possible brightness value is 255
#define BRIGHTNESS_MAX 255
// The M5Stack's backlight is on Channel 7
#define BACKLIGHT_CHANNEL LEDC_CHANNEL_7

extern const char * TAG;

uint16_t DisplayHeight = 0;
uint16_t DisplayWidth  = 0;

#if CONFIG_DISPLAY_AUTO_OFF
// FreeRTOS timer used to turn the display off after a short while
TimerHandle_t displayTimer = NULL;
#endif

static void TimerCallback(TimerHandle_t xTimer);
static void SetupBrightnessControl();
static void SetBrightness(uint16_t brightness_percent);

esp_err_t InitDisplay()
{
    esp_err_t err;
    spi_lobo_device_handle_t spi;

    // configured based on the display driver's examples
    spi_lobo_bus_config_t buscfg;
    memset((void *) &buscfg, 0, sizeof(buscfg));
    buscfg.miso_io_num   = PIN_NUM_MISO; // set SPI MISO pin
    buscfg.mosi_io_num   = PIN_NUM_MOSI; // set SPI MOSI pin
    buscfg.sclk_io_num   = PIN_NUM_CLK;  // set SPI CLK pin
    buscfg.quadwp_io_num = -1;
    buscfg.quadhd_io_num = -1;

    spi_lobo_device_interface_config_t devcfg;
    memset((void *) &devcfg, 0, sizeof(devcfg));
    devcfg.clock_speed_hz   = TFT_SPI_CLOCK_INIT_HZ;
    devcfg.mode             = 0;                        // SPI mode 0
    devcfg.spics_io_num     = -1;                       // we will use external CS pin
    devcfg.spics_ext_io_num = PIN_NUM_CS;               // external CS pin
    devcfg.flags            = LB_SPI_DEVICE_HALFDUPLEX; // ALWAYS SET  to HALF DUPLEX MODE!! for display spi
    tft_max_rdclock         = TFT_SPI_CLOCK_INIT_HZ;

    // Initialize all pins used by display driver.
    TFT_PinsInit();

    // Initialize SPI bus and add a device for the display.
    err = spi_lobo_bus_add_device(TFT_HSPI_HOST, &buscfg, &devcfg, &spi);
    if (err != ESP_OK)
        return err;

    // Configure the display to use the new SPI device.
    tft_disp_spi = spi;

    err = spi_lobo_device_select(spi, 1);
    if (err != ESP_OK)
        return err;
    err = spi_lobo_device_deselect(spi);
    if (err != ESP_OK)
        return err;

    // Initialize the display driver.
    TFT_display_init();

    // Detect maximum read speed and set it.
    tft_max_rdclock = find_rd_speed();

    // Set the SPI clock speed overriding the initialized 8MHz speed
    spi_lobo_set_speed(spi, DEFAULT_SPI_CLOCK);

    TFT_setGammaCurve(0);
    TFT_setRotation(LANDSCAPE);
    TFT_resetclipwin();

    DisplayWidth  = (uint16_t)(1 + tft_dispWin.x2 - tft_dispWin.x1);
    DisplayHeight = (uint16_t)(1 + tft_dispWin.y2 - tft_dispWin.y1);

    ESP_LOGI(TAG, "Display initialized (height %u, width %u)", DisplayHeight, DisplayWidth);

    // for some reason this is backwards (turns out this is because of a 2019 update to the m5stack hw)
    TFT_invertDisplay(INVERT_ON);

    // prepare the display for brightness control
    SetupBrightnessControl();

#if CONFIG_DISPLAY_AUTO_OFF
    displayTimer = xTimerCreate("DisplayTimer", pdMS_TO_TICKS(DISPLAY_TIMEOUT_MS), false, NULL, TimerCallback);
#endif
    // lower the brightness of the screen
    WakeDisplay();

    return err;
}

void SetBrightness(uint16_t brightness_percent)
{
    uint16_t brightness = (brightness_percent * BRIGHTNESS_MAX) / 100;
    if (ledc_set_duty(LEDC_HIGH_SPEED_MODE, BACKLIGHT_CHANNEL, brightness) ||
        ledc_update_duty(LEDC_HIGH_SPEED_MODE, BACKLIGHT_CHANNEL))
    {
        ESP_LOGE(TAG, "Failed to set display brightness...");
    }
}

void WakeDisplay()
{
    SetBrightness(DEFFAULT_BRIGHTNESS_PERCENT);
#if CONFIG_DISPLAY_AUTO_OFF
    xTimerStart(displayTimer, 0);
    ESP_LOGI(TAG, "Display awake but will switch off automatically in %d seconds", DISPLAY_TIMEOUT_MS / 1000);
#endif
}

void ClearDisplay()
{
    ClearRect();
}

void ClearRect(uint16_t x_percent_start, uint16_t y_percent_start, uint16_t x_percent_end, uint16_t y_percent_end)
{
    if (x_percent_end < x_percent_start)
    {
        x_percent_end = x_percent_start;
    }
    if (y_percent_end < y_percent_start)
    {
        y_percent_end = y_percent_start;
    }
    uint16_t start_x = (DisplayWidth * x_percent_start) / 100;
    uint16_t start_y = (DisplayHeight * y_percent_start) / 100;
    uint16_t end_x   = (DisplayWidth * x_percent_end) / 100;
    uint16_t end_y   = (DisplayHeight * y_percent_end) / 100;
    TFT_fillRect(start_x, start_y, end_x, end_y, TFT_BLACK);
}

void DisplayStatusMessage(char * msg, uint16_t vpos)
{
    TFT_setFont(SMALL_FONT, NULL);
    uint16_t msgX = 0;
    uint16_t msgY = (DisplayHeight * vpos) / 100;
    TFT_print(msg, msgX, msgY);
}

void TimerCallback(TimerHandle_t xTimer)
{
    ESP_LOGI(TAG, "Display going to sleep...");
    SetBrightness(0);
}

void SetupBrightnessControl()
{
    ledc_timer_config_t ledc_timer;
    memset(&ledc_timer, 0, sizeof(ledc_timer));

    ledc_timer.duty_resolution = LEDC_TIMER_8_BIT;     // resolution of PWM duty
    ledc_timer.freq_hz         = LEDC_PWM_HZ;          // frequency of PWM signal
    ledc_timer.speed_mode      = LEDC_HIGH_SPEED_MODE; // timer mode
    ledc_timer.timer_num       = LEDC_TIMER_0;         // timer index
    ledc_timer_config(&ledc_timer);

    ledc_timer_set(LEDC_HIGH_SPEED_MODE, LEDC_TIMER_0, LEDC_PWM_HZ, LEDC_TIMER_8_BIT, LEDC_REF_TICK);

    ledc_channel_config_t ledc_channel;
    memset(&ledc_channel, 0, sizeof(ledc_channel));
    ledc_channel.channel    = BACKLIGHT_CHANNEL;
    ledc_channel.duty       = BRIGHTNESS_MAX;
    ledc_channel.gpio_num   = PIN_NUM_BCKL;
    ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;
    ledc_channel.timer_sel  = LEDC_TIMER_0;
    ledc_channel_config(&ledc_channel);
}

#endif // CONFIG_HAVE_DISPLAY
<commit_msg>warning: 'void TimerCallback(TimerHandle_t)' defined but not used (#1367)<commit_after>/*
 *
 *    Copyright (c) 2020 Project CHIP Authors
 *    Copyright (c) 2018 Nest Labs, Inc.
 *    All rights reserved.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 * @file Display.cpp
 *
 * This file implements helper APIs for the M5Stack's display
 *
 */

#include <string.h>

#include "driver/ledc.h"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"

#include "Display.h"

#if CONFIG_HAVE_DISPLAY

// Brightness picked such that it's easy for cameras to focus on
#define DEFFAULT_BRIGHTNESS_PERCENT 10

// 8MHz is the recommended SPI speed to init the driver with
// It later gets set to the preconfigured defaults within the driver
#define TFT_SPI_CLOCK_INIT_HZ 8000000

// The frequency used by the ledc timer
// value chosen to eliminate flicker
#define LEDC_PWM_HZ 1000

// with a duty resolution of LEDC_TIMER_8_BIT
// the highest possible brightness value is 255
#define BRIGHTNESS_MAX 255
// The M5Stack's backlight is on Channel 7
#define BACKLIGHT_CHANNEL LEDC_CHANNEL_7

extern const char * TAG;

uint16_t DisplayHeight = 0;
uint16_t DisplayWidth  = 0;

#if CONFIG_DISPLAY_AUTO_OFF
// FreeRTOS timer used to turn the display off after a short while
TimerHandle_t displayTimer = NULL;
#endif

#if CONFIG_DISPLAY_AUTO_OFF
static void TimerCallback(TimerHandle_t xTimer);
#endif
static void SetupBrightnessControl();
static void SetBrightness(uint16_t brightness_percent);

esp_err_t InitDisplay()
{
    esp_err_t err;
    spi_lobo_device_handle_t spi;

    // configured based on the display driver's examples
    spi_lobo_bus_config_t buscfg;
    memset((void *) &buscfg, 0, sizeof(buscfg));
    buscfg.miso_io_num   = PIN_NUM_MISO; // set SPI MISO pin
    buscfg.mosi_io_num   = PIN_NUM_MOSI; // set SPI MOSI pin
    buscfg.sclk_io_num   = PIN_NUM_CLK;  // set SPI CLK pin
    buscfg.quadwp_io_num = -1;
    buscfg.quadhd_io_num = -1;

    spi_lobo_device_interface_config_t devcfg;
    memset((void *) &devcfg, 0, sizeof(devcfg));
    devcfg.clock_speed_hz   = TFT_SPI_CLOCK_INIT_HZ;
    devcfg.mode             = 0;                        // SPI mode 0
    devcfg.spics_io_num     = -1;                       // we will use external CS pin
    devcfg.spics_ext_io_num = PIN_NUM_CS;               // external CS pin
    devcfg.flags            = LB_SPI_DEVICE_HALFDUPLEX; // ALWAYS SET  to HALF DUPLEX MODE!! for display spi
    tft_max_rdclock         = TFT_SPI_CLOCK_INIT_HZ;

    // Initialize all pins used by display driver.
    TFT_PinsInit();

    // Initialize SPI bus and add a device for the display.
    err = spi_lobo_bus_add_device(TFT_HSPI_HOST, &buscfg, &devcfg, &spi);
    if (err != ESP_OK)
        return err;

    // Configure the display to use the new SPI device.
    tft_disp_spi = spi;

    err = spi_lobo_device_select(spi, 1);
    if (err != ESP_OK)
        return err;
    err = spi_lobo_device_deselect(spi);
    if (err != ESP_OK)
        return err;

    // Initialize the display driver.
    TFT_display_init();

    // Detect maximum read speed and set it.
    tft_max_rdclock = find_rd_speed();

    // Set the SPI clock speed overriding the initialized 8MHz speed
    spi_lobo_set_speed(spi, DEFAULT_SPI_CLOCK);

    TFT_setGammaCurve(0);
    TFT_setRotation(LANDSCAPE);
    TFT_resetclipwin();

    DisplayWidth  = (uint16_t)(1 + tft_dispWin.x2 - tft_dispWin.x1);
    DisplayHeight = (uint16_t)(1 + tft_dispWin.y2 - tft_dispWin.y1);

    ESP_LOGI(TAG, "Display initialized (height %u, width %u)", DisplayHeight, DisplayWidth);

    // for some reason this is backwards (turns out this is because of a 2019 update to the m5stack hw)
    TFT_invertDisplay(INVERT_ON);

    // prepare the display for brightness control
    SetupBrightnessControl();

#if CONFIG_DISPLAY_AUTO_OFF
    displayTimer = xTimerCreate("DisplayTimer", pdMS_TO_TICKS(DISPLAY_TIMEOUT_MS), false, NULL, TimerCallback);
#endif
    // lower the brightness of the screen
    WakeDisplay();

    return err;
}

void SetBrightness(uint16_t brightness_percent)
{
    uint16_t brightness = (brightness_percent * BRIGHTNESS_MAX) / 100;
    if (ledc_set_duty(LEDC_HIGH_SPEED_MODE, BACKLIGHT_CHANNEL, brightness) ||
        ledc_update_duty(LEDC_HIGH_SPEED_MODE, BACKLIGHT_CHANNEL))
    {
        ESP_LOGE(TAG, "Failed to set display brightness...");
    }
}

void WakeDisplay()
{
    SetBrightness(DEFFAULT_BRIGHTNESS_PERCENT);
#if CONFIG_DISPLAY_AUTO_OFF
    xTimerStart(displayTimer, 0);
    ESP_LOGI(TAG, "Display awake but will switch off automatically in %d seconds", DISPLAY_TIMEOUT_MS / 1000);
#endif
}

void ClearDisplay()
{
    ClearRect();
}

void ClearRect(uint16_t x_percent_start, uint16_t y_percent_start, uint16_t x_percent_end, uint16_t y_percent_end)
{
    if (x_percent_end < x_percent_start)
    {
        x_percent_end = x_percent_start;
    }
    if (y_percent_end < y_percent_start)
    {
        y_percent_end = y_percent_start;
    }
    uint16_t start_x = (DisplayWidth * x_percent_start) / 100;
    uint16_t start_y = (DisplayHeight * y_percent_start) / 100;
    uint16_t end_x   = (DisplayWidth * x_percent_end) / 100;
    uint16_t end_y   = (DisplayHeight * y_percent_end) / 100;
    TFT_fillRect(start_x, start_y, end_x, end_y, TFT_BLACK);
}

void DisplayStatusMessage(char * msg, uint16_t vpos)
{
    TFT_setFont(SMALL_FONT, NULL);
    uint16_t msgX = 0;
    uint16_t msgY = (DisplayHeight * vpos) / 100;
    TFT_print(msg, msgX, msgY);
}

void TimerCallback(TimerHandle_t xTimer)
{
    ESP_LOGI(TAG, "Display going to sleep...");
    SetBrightness(0);
}

void SetupBrightnessControl()
{
    ledc_timer_config_t ledc_timer;
    memset(&ledc_timer, 0, sizeof(ledc_timer));

    ledc_timer.duty_resolution = LEDC_TIMER_8_BIT;     // resolution of PWM duty
    ledc_timer.freq_hz         = LEDC_PWM_HZ;          // frequency of PWM signal
    ledc_timer.speed_mode      = LEDC_HIGH_SPEED_MODE; // timer mode
    ledc_timer.timer_num       = LEDC_TIMER_0;         // timer index
    ledc_timer_config(&ledc_timer);

    ledc_timer_set(LEDC_HIGH_SPEED_MODE, LEDC_TIMER_0, LEDC_PWM_HZ, LEDC_TIMER_8_BIT, LEDC_REF_TICK);

    ledc_channel_config_t ledc_channel;
    memset(&ledc_channel, 0, sizeof(ledc_channel));
    ledc_channel.channel    = BACKLIGHT_CHANNEL;
    ledc_channel.duty       = BRIGHTNESS_MAX;
    ledc_channel.gpio_num   = PIN_NUM_BCKL;
    ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;
    ledc_channel.timer_sel  = LEDC_TIMER_0;
    ledc_channel_config(&ledc_channel);
}

#endif // CONFIG_HAVE_DISPLAY
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2007-2008, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include <boost/format.hpp>

#include "IECore/ImageCropOp.h"
#include "IECore/SimpleTypedData.h"
#include "IECore/VectorTypedData.h"
#include "IECore/TransformationMatrixData.h"
#include "IECore/TypedObjectParameter.h"
#include "IECore/CompoundParameter.h"
#include "IECore/CompoundObject.h"
#include "IECore/BoxOps.h"
#include "IECore/ImagePrimitive.h"
#include "IECore/DespatchTypedData.h"

#include <cassert>

using namespace IECore;
using namespace Imath;
using namespace std;
using namespace boost;

ImageCropOp::ImageCropOp()
	:	ModifyOp(
		staticTypeName(),
		"Performs cropping over ImagePrimitive objects.\n"
		"The operation results on an ImagePrimitive with displayWindow equal to the intersection of the given crop box and the original image displayWindow.\n"
		"If matchDataWindow if On then the dataWindow will match the new displayWindow (new pixels will be filled with zero). Otherwise it will only be intersected against the given crop box.",
		new ImagePrimitiveParameter(
			"result",
			"Cropped image.",
			new ImagePrimitive()
		),
		new ImagePrimitiveParameter(
			"object",
			"The vector object that will be transformed by the matrix.",
			new ImagePrimitive()
		)
	)
{
	m_cropBox = new Box2iParameter(
		"cropBox",
		"Determines the crop coordinates to apply on the image.",
		Box2i()
	);

	parameters()->addParameter( m_cropBox );

	m_matchDataWindow = new BoolParameter(
		"matchDataWindow",
		"if On then the dataWindow will match displayWindow. Otherwise it will be intersected against the given crop box.",

		/// \todo The original intent was that this default should be false, but when specifying "new BoolData(false)" as
		/// the default value it seems the compiler would prefer to convert the BoolData* to a bool over a BoolData::Ptr.
		/// Consider changing the default if required, but only on the next major version change.
		true
	);

	parameters()->addParameter( m_matchDataWindow );

	m_resetOrigin = new BoolParameter(
		"resetOrigin",
		"if On then the resulting image will have it's top-left corner at (0,0).",
		true
	);

	parameters()->addParameter( m_resetOrigin );

	/// \todo Add "intersect" and "reformat" parameters, like Nuke
	/// Current behaviour is to both intersect, and reformat, so take care to set
	/// the defaults accordingly.
}

ImageCropOp::~ImageCropOp()
{
}

Box2iParameterPtr ImageCropOp::cropBoxParameter()
{
	return m_cropBox;
}

ConstBox2iParameterPtr ImageCropOp::cropBoxParameter() const
{
	return m_cropBox;
}

BoolParameterPtr ImageCropOp::matchDataWindowParameter()
{
	return m_matchDataWindow;
}

ConstBoolParameterPtr ImageCropOp::matchDataWindowParameter() const
{
	return m_matchDataWindow;
}

BoolParameterPtr ImageCropOp::resetOriginParameter()
{
	return m_resetOrigin;
}

ConstBoolParameterPtr ImageCropOp::resetOriginParameter() const
{
	return m_resetOrigin;
}

struct ImageCropOp::ImageCropFn
{
	typedef DataPtr ReturnType;

	const Imath::Box2i m_sourceDataWindow;
	const Imath::Box2i m_croppedDataWindow;
	const Imath::Box2i m_targetDataWindow;

	ImageCropFn( const Imath::Box2i &sourceDataWindow, const Imath::Box2i &croppedDataWindow, const Imath::Box2i &targetDataWindow )
	: m_sourceDataWindow( sourceDataWindow ), m_croppedDataWindow( croppedDataWindow ), m_targetDataWindow( targetDataWindow )
	{
	}

	template< typename T>
	ReturnType operator()( typename T::ConstPtr sourceData ) const
	{
		assert( sourceData );

		typename T::Ptr newChannel = new T();

		int sourceWidth = m_sourceDataWindow.max.x - m_sourceDataWindow.min.x + 1;

		int targetWidth = m_targetDataWindow.max.x - m_targetDataWindow.min.x + 1;
		int targetHeight = m_targetDataWindow.max.y - m_targetDataWindow.min.y + 1;
		int targetArea = targetWidth * targetHeight;

#ifndef NDEBUG
		int numPixelsCopied = 0;
#endif

		typename T::ValueType &target = newChannel->writable();
		target.resize( targetArea, typename T::ValueType::value_type() );

		const typename T::ValueType &source = sourceData->readable();

		/// \todo Optimize
		Imath::V2i copyPixel;
		for ( copyPixel.y = m_croppedDataWindow.min.y; copyPixel.y <= m_croppedDataWindow.max.y; copyPixel.y ++ )
		{
			for ( copyPixel.x = m_croppedDataWindow.min.x; copyPixel.x <= m_croppedDataWindow.max.x; copyPixel.x ++ )
			{
				if ( m_sourceDataWindow.intersects( copyPixel ) && m_targetDataWindow.intersects( copyPixel ) )
				{
					assert( boxIntersection( m_sourceDataWindow, m_targetDataWindow ).intersects( copyPixel ) );

					int sourceOffset = sourceWidth * ( copyPixel.y - m_sourceDataWindow.min.y ) + ( copyPixel.x - m_sourceDataWindow.min.x );
					assert( sourceOffset >= 0 );
					assert( sourceOffset < (int)source.size() );
					int targetOffset = targetWidth * ( copyPixel.y - m_targetDataWindow.min.y ) + ( copyPixel.x - m_targetDataWindow.min.x );
					assert( targetOffset >= 0 );
					assert( targetOffset < (int)target.size() );

					target[ targetOffset++ ] = source[ sourceOffset++ ];
#ifndef NDEBUG
					numPixelsCopied ++;
#endif
				}
				else
				{
					assert( !boxIntersection( m_sourceDataWindow, m_targetDataWindow ).intersects( copyPixel ) );
				}
			}
		}

		assert( (int)target.size() == targetArea );
		assert( numPixelsCopied <= (int)target.size() );

		assert( newChannel );
		return newChannel;
	}

};

void ImageCropOp::modify( ObjectPtr toModify, ConstCompoundObjectPtr operands )
{
	ImagePrimitivePtr image = assertedStaticCast< ImagePrimitive >( toModify );

	// Validate the input image
	if ( !image->arePrimitiveVariablesValid() )
	{
		throw InvalidArgumentException( "ImageCropOp: Input image is not valid" );
	}

	const Imath::Box2i &cropBox = m_cropBox->getTypedValue();
	if ( cropBox.isEmpty() )
	{
		throw InvalidArgumentException( "ImageCropOp: Specified crop box is empty" );
	}

	const bool resetOrigin = m_resetOrigin->getTypedValue();

	const Imath::Box2i croppedDisplayWindow = boxIntersection( cropBox, image->getDisplayWindow() );
	const Imath::Box2i &dataWindow = image->getDataWindow();

	Imath::Box2i croppedDataWindow = boxIntersection( cropBox, dataWindow );

	Imath::Box2i newDisplayWindow = croppedDisplayWindow;
	Imath::Box2i newDataWindow;

	const bool matchDataWindow = m_matchDataWindow->getTypedValue();
	if ( matchDataWindow )
	{
		newDataWindow = newDisplayWindow;
	}
	else
	{
		newDataWindow = croppedDataWindow;
	}

	for ( PrimitiveVariableMap::iterator varIt = image->variables.begin(); varIt != image->variables.end(); varIt++ )
	{
		PrimitiveVariable &channel = varIt->second;

		switch ( channel.interpolation )
		{
			case PrimitiveVariable::Vertex:
			case PrimitiveVariable::Varying:
			case PrimitiveVariable::FaceVarying:
				{
					DataPtr data = channel.data;
					assert( data );
					ImageCropFn fn( dataWindow, croppedDataWindow, newDataWindow );

					channel.data = despatchTypedData< ImageCropFn, TypeTraits::IsVectorTypedData >( data, fn );
					assert( channel.data );
				}
				break;

			default:
				/// Nothing to do for these channel types
				assert( channel.interpolation == PrimitiveVariable::Constant || channel.interpolation == PrimitiveVariable::Uniform );
				break;
		}
	}

	if ( resetOrigin )
	{
#ifndef NDEBUG
		V2i newDisplayWindowSize = newDisplayWindow.size();
		V2i newDataWindowSize = newDataWindow.size();
#endif
		newDisplayWindow.max = newDisplayWindow.max - newDisplayWindow.min;
		newDataWindow.min = newDataWindow.min - newDisplayWindow.min;
		newDataWindow.max = newDataWindow.max - newDisplayWindow.min;
		newDisplayWindow.min = Imath::V2i( 0, 0 );
		assert( newDisplayWindowSize == newDisplayWindow.size() );
		assert( newDataWindowSize == newDataWindow.size() );
	}
	image->setDataWindow( newDataWindow );
	image->setDisplayWindow( newDisplayWindow );

	if ( matchDataWindow )
	{
		assert( image->getDisplayWindow() == image->getDataWindow() );
	}

	assert( image->arePrimitiveVariablesValid() );
}

<commit_msg>Gone back to numeric data only, because ImagePrimitivse are loosely defined this way, and so that we can zero-initialize consistently<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2007-2008, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include <boost/format.hpp>

#include "IECore/ImageCropOp.h"
#include "IECore/SimpleTypedData.h"
#include "IECore/VectorTypedData.h"
#include "IECore/TransformationMatrixData.h"
#include "IECore/TypedObjectParameter.h"
#include "IECore/CompoundParameter.h"
#include "IECore/CompoundObject.h"
#include "IECore/BoxOps.h"
#include "IECore/ImagePrimitive.h"
#include "IECore/DespatchTypedData.h"

#include <cassert>

using namespace IECore;
using namespace Imath;
using namespace std;
using namespace boost;

ImageCropOp::ImageCropOp()
	:	ModifyOp(
		staticTypeName(),
		"Performs cropping over ImagePrimitive objects.\n"
		"The operation results on an ImagePrimitive with displayWindow equal to the intersection of the given crop box and the original image displayWindow.\n"
		"If matchDataWindow if On then the dataWindow will match the new displayWindow (new pixels will be filled with zero). Otherwise it will only be intersected against the given crop box.",
		new ImagePrimitiveParameter(
			"result",
			"Cropped image.",
			new ImagePrimitive()
		),
		new ImagePrimitiveParameter(
			"object",
			"The vector object that will be transformed by the matrix.",
			new ImagePrimitive()
		)
	)
{
	m_cropBox = new Box2iParameter(
		"cropBox",
		"Determines the crop coordinates to apply on the image.",
		Box2i()
	);

	parameters()->addParameter( m_cropBox );

	m_matchDataWindow = new BoolParameter(
		"matchDataWindow",
		"if On then the dataWindow will match displayWindow. Otherwise it will be intersected against the given crop box.",

		/// \todo The original intent was that this default should be false, but when specifying "new BoolData(false)" as
		/// the default value it seems the compiler would prefer to convert the BoolData* to a bool over a BoolData::Ptr.
		/// Consider changing the default if required, but only on the next major version change.
		true
	);

	parameters()->addParameter( m_matchDataWindow );

	m_resetOrigin = new BoolParameter(
		"resetOrigin",
		"if On then the resulting image will have it's top-left corner at (0,0).",
		true
	);

	parameters()->addParameter( m_resetOrigin );

	/// \todo Add "intersect" and "reformat" parameters, like Nuke
	/// Current behaviour is to both intersect, and reformat, so take care to set
	/// the defaults accordingly.
}

ImageCropOp::~ImageCropOp()
{
}

Box2iParameterPtr ImageCropOp::cropBoxParameter()
{
	return m_cropBox;
}

ConstBox2iParameterPtr ImageCropOp::cropBoxParameter() const
{
	return m_cropBox;
}

BoolParameterPtr ImageCropOp::matchDataWindowParameter()
{
	return m_matchDataWindow;
}

ConstBoolParameterPtr ImageCropOp::matchDataWindowParameter() const
{
	return m_matchDataWindow;
}

BoolParameterPtr ImageCropOp::resetOriginParameter()
{
	return m_resetOrigin;
}

ConstBoolParameterPtr ImageCropOp::resetOriginParameter() const
{
	return m_resetOrigin;
}

struct ImageCropOp::ImageCropFn
{
	typedef DataPtr ReturnType;

	const Imath::Box2i m_sourceDataWindow;
	const Imath::Box2i m_croppedDataWindow;
	const Imath::Box2i m_targetDataWindow;

	ImageCropFn( const Imath::Box2i &sourceDataWindow, const Imath::Box2i &croppedDataWindow, const Imath::Box2i &targetDataWindow )
	: m_sourceDataWindow( sourceDataWindow ), m_croppedDataWindow( croppedDataWindow ), m_targetDataWindow( targetDataWindow )
	{
	}

	template< typename T>
	ReturnType operator()( typename T::ConstPtr sourceData ) const
	{
		assert( sourceData );

		typename T::Ptr newChannel = new T();

		int sourceWidth = m_sourceDataWindow.max.x - m_sourceDataWindow.min.x + 1;

		int targetWidth = m_targetDataWindow.max.x - m_targetDataWindow.min.x + 1;
		int targetHeight = m_targetDataWindow.max.y - m_targetDataWindow.min.y + 1;
		int targetArea = targetWidth * targetHeight;

#ifndef NDEBUG
		int numPixelsCopied = 0;
#endif

		typename T::ValueType &target = newChannel->writable();
		target.resize( targetArea, 0 );

		const typename T::ValueType &source = sourceData->readable();

		/// \todo Optimize
		Imath::V2i copyPixel;
		for ( copyPixel.y = m_croppedDataWindow.min.y; copyPixel.y <= m_croppedDataWindow.max.y; copyPixel.y ++ )
		{
			for ( copyPixel.x = m_croppedDataWindow.min.x; copyPixel.x <= m_croppedDataWindow.max.x; copyPixel.x ++ )
			{
				if ( m_sourceDataWindow.intersects( copyPixel ) && m_targetDataWindow.intersects( copyPixel ) )
				{
					assert( boxIntersection( m_sourceDataWindow, m_targetDataWindow ).intersects( copyPixel ) );

					int sourceOffset = sourceWidth * ( copyPixel.y - m_sourceDataWindow.min.y ) + ( copyPixel.x - m_sourceDataWindow.min.x );
					assert( sourceOffset >= 0 );
					assert( sourceOffset < (int)source.size() );
					int targetOffset = targetWidth * ( copyPixel.y - m_targetDataWindow.min.y ) + ( copyPixel.x - m_targetDataWindow.min.x );
					assert( targetOffset >= 0 );
					assert( targetOffset < (int)target.size() );

					target[ targetOffset++ ] = source[ sourceOffset++ ];
#ifndef NDEBUG
					numPixelsCopied ++;
#endif
				}
				else
				{
					assert( !boxIntersection( m_sourceDataWindow, m_targetDataWindow ).intersects( copyPixel ) );
				}
			}
		}

		assert( (int)target.size() == targetArea );
		assert( numPixelsCopied <= (int)target.size() );

		assert( newChannel );
		return newChannel;
	}

};

void ImageCropOp::modify( ObjectPtr toModify, ConstCompoundObjectPtr operands )
{
	ImagePrimitivePtr image = assertedStaticCast< ImagePrimitive >( toModify );

	// Validate the input image
	if ( !image->arePrimitiveVariablesValid() )
	{
		throw InvalidArgumentException( "ImageCropOp: Input image is not valid" );
	}

	const Imath::Box2i &cropBox = m_cropBox->getTypedValue();
	if ( cropBox.isEmpty() )
	{
		throw InvalidArgumentException( "ImageCropOp: Specified crop box is empty" );
	}

	const bool resetOrigin = m_resetOrigin->getTypedValue();

	const Imath::Box2i croppedDisplayWindow = boxIntersection( cropBox, image->getDisplayWindow() );
	const Imath::Box2i &dataWindow = image->getDataWindow();

	Imath::Box2i croppedDataWindow = boxIntersection( cropBox, dataWindow );

	Imath::Box2i newDisplayWindow = croppedDisplayWindow;
	Imath::Box2i newDataWindow;

	const bool matchDataWindow = m_matchDataWindow->getTypedValue();
	if ( matchDataWindow )
	{
		newDataWindow = newDisplayWindow;
	}
	else
	{
		newDataWindow = croppedDataWindow;
	}

	for ( PrimitiveVariableMap::iterator varIt = image->variables.begin(); varIt != image->variables.end(); varIt++ )
	{
		PrimitiveVariable &channel = varIt->second;

		switch ( channel.interpolation )
		{
			case PrimitiveVariable::Vertex:
			case PrimitiveVariable::Varying:
			case PrimitiveVariable::FaceVarying:
				{
					DataPtr data = channel.data;
					assert( data );
					ImageCropFn fn( dataWindow, croppedDataWindow, newDataWindow );

					channel.data = despatchTypedData< ImageCropFn, TypeTraits::IsNumericVectorTypedData >( data, fn );
					assert( channel.data );
				}
				break;

			default:
				/// Nothing to do for these channel types
				assert( channel.interpolation == PrimitiveVariable::Constant || channel.interpolation == PrimitiveVariable::Uniform );
				break;
		}
	}

	if ( resetOrigin )
	{
#ifndef NDEBUG
		V2i newDisplayWindowSize = newDisplayWindow.size();
		V2i newDataWindowSize = newDataWindow.size();
#endif
		newDisplayWindow.max = newDisplayWindow.max - newDisplayWindow.min;
		newDataWindow.min = newDataWindow.min - newDisplayWindow.min;
		newDataWindow.max = newDataWindow.max - newDisplayWindow.min;
		newDisplayWindow.min = Imath::V2i( 0, 0 );
		assert( newDisplayWindowSize == newDisplayWindow.size() );
		assert( newDataWindowSize == newDataWindow.size() );
	}
	image->setDataWindow( newDataWindow );
	image->setDisplayWindow( newDisplayWindow );

	if ( matchDataWindow )
	{
		assert( image->getDisplayWindow() == image->getDataWindow() );
	}

	assert( image->arePrimitiveVariablesValid() );
}

<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: readguard.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: as $ $Date: 2001-06-11 10:24:13 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef __FRAMEWORK_THREADHELP_READGUARD_HXX_
#define __FRAMEWORK_THREADHELP_READGUARD_HXX_

//_________________________________________________________________________________________________________________
//  my own includes
//_________________________________________________________________________________________________________________

#ifndef __FRAMEWORK_THREADHELP_INONCOPYABLE_H_
#include <threadhelp/inoncopyable.h>
#endif

#ifndef __FRAMEWORK_THREADHELP_IRWLOCK_H_
#include <threadhelp/irwlock.h>
#endif

//#ifndef __FRAMEWORK_THREADHELP_THREADHELPBASE_HXX_
//#include <threadhelp/threadhelpbase.hxx>
//#endif

//_________________________________________________________________________________________________________________
//  interface includes
//_________________________________________________________________________________________________________________

//_________________________________________________________________________________________________________________
//  other includes
//_________________________________________________________________________________________________________________

#ifndef _SAL_TYPES_H_
#include <sal/types.h>
#endif

//_________________________________________________________________________________________________________________
//  namespace
//_________________________________________________________________________________________________________________

namespace framework{

//_________________________________________________________________________________________________________________
//  const
//_________________________________________________________________________________________________________________

//_________________________________________________________________________________________________________________
//  declarations
//_________________________________________________________________________________________________________________

/*-************************************************************************************************************//**
    @short          implement a guard to set read locks
    @descr          This guard should be used to set a lock for reading object internal member.
                    Nobody can control it but don't use member after successfuly locking for writing!
                    We never need a own mutex to safe our internal member access - because
                    a guard is used as function-local member only. There exist no multithreaded access to it realy ...

    @attention      a) To prevent us against wrong using, the default ctor, copy ctor and the =operator are maked private!
                    b) Use interface "IRWLock" of set LockHelper only - because we must support a finer granularity of locking.
                       Interface "IMutex" should be used by easier guard implementations ... like "ResetableGuard"!

    @implements     -
    @base           INonCopyable

    @devstatus      ready to use
*//*-*************************************************************************************************************/
class ReadGuard : private INonCopyable
{
    //-------------------------------------------------------------------------------------------------------------
    //  public methods
    //-------------------------------------------------------------------------------------------------------------
    public:

        /*-****************************************************************************************************//**
            @short      ctor
            @descr      These ctors initialize the guard with a reference to used lock member of object to protect.
                        Null isn't allowed as value!

            @seealso    -

            @param      "pLock" ,reference to used lock member of object to protect
            @param      "rLock" ,reference to used lock member of object to protect
            @return     -

            @onerror    -
        *//*-*****************************************************************************************************/
        inline ReadGuard( IRWLock* pLock )
            :   m_pLock     ( pLock     )
            ,   m_bLocked   ( sal_False )
        {
            lock();
        }

        //*********************************************************************************************************
        inline ReadGuard( IRWLock& rLock )
            :   m_pLock     ( &rLock    )
            ,   m_bLocked   ( sal_False )
        {
            lock();
        }

        /*-****************************************************************************************************//**
            @short      dtor
            @descr      We unlock the used lock member automaticly if user forget it.

            @seealso    -

            @param      -
            @return     -

            @onerror    -
        *//*-*****************************************************************************************************/
        inline ~ReadGuard()
        {
            unlock();
        }

        /*-****************************************************************************************************//**
            @short      set read lock
            @descr      Call this method to set the read lock. The call will block till all current threads are synchronized!

            @seealso    method unlock()

            @param      -
            @return     -

            @onerror    -
        *//*-*****************************************************************************************************/
        inline void lock()
        {
            if( m_bLocked == sal_False )
            {
                m_pLock->acquireReadAccess();
                m_bLocked = sal_True;
            }
        }

        /*-****************************************************************************************************//**
            @short      unset read lock
            @descr      Call this method to unlock the rw-lock temp.!
                        Normaly we do it at dtor automaticly for you ...

            @seealso    method lock()

            @param      -
            @return     -

            @onerror    -
        *//*-*****************************************************************************************************/
        inline void unlock()
        {
            if( m_bLocked == sal_True )
            {
                m_pLock->releaseReadAccess();
                m_bLocked = sal_False;
            }
        }

    //-------------------------------------------------------------------------------------------------------------
    //  private methods
    //-------------------------------------------------------------------------------------------------------------
    private:

        /*-****************************************************************************************************//**
            @short      disable using of these functions!
            @descr      It's not allowed to use this methods. Different problem can occure otherwise.
                        Thats why we disable it by make it private.

            @seealso    other ctor

            @param      -
            @return     -

            @onerror    -
        *//*-*****************************************************************************************************/
        ReadGuard();

    //-------------------------------------------------------------------------------------------------------------
    //  private member
    //-------------------------------------------------------------------------------------------------------------
    private:

        IRWLock*    m_pLock     ;   /// reference to lock-member of protected object
        sal_Bool    m_bLocked   ;   /// protection against multiple lock calls without unlock!

};      //  class ReadGuard

}       //  namespace framework

#endif  //  #ifndef __FRAMEWORK_THREADHELP_READGUARD_HXX_
<commit_msg>INTEGRATION: CWS ooo19126 (1.4.584); FILE MERGED 2005/09/05 13:05:24 rt 1.4.584.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: readguard.hxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 00:37:28 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef __FRAMEWORK_THREADHELP_READGUARD_HXX_
#define __FRAMEWORK_THREADHELP_READGUARD_HXX_

//_________________________________________________________________________________________________________________
//  my own includes
//_________________________________________________________________________________________________________________

#ifndef __FRAMEWORK_THREADHELP_INONCOPYABLE_H_
#include <threadhelp/inoncopyable.h>
#endif

#ifndef __FRAMEWORK_THREADHELP_IRWLOCK_H_
#include <threadhelp/irwlock.h>
#endif

//#ifndef __FRAMEWORK_THREADHELP_THREADHELPBASE_HXX_
//#include <threadhelp/threadhelpbase.hxx>
//#endif

//_________________________________________________________________________________________________________________
//  interface includes
//_________________________________________________________________________________________________________________

//_________________________________________________________________________________________________________________
//  other includes
//_________________________________________________________________________________________________________________

#ifndef _SAL_TYPES_H_
#include <sal/types.h>
#endif

//_________________________________________________________________________________________________________________
//  namespace
//_________________________________________________________________________________________________________________

namespace framework{

//_________________________________________________________________________________________________________________
//  const
//_________________________________________________________________________________________________________________

//_________________________________________________________________________________________________________________
//  declarations
//_________________________________________________________________________________________________________________

/*-************************************************************************************************************//**
    @short          implement a guard to set read locks
    @descr          This guard should be used to set a lock for reading object internal member.
                    Nobody can control it but don't use member after successfuly locking for writing!
                    We never need a own mutex to safe our internal member access - because
                    a guard is used as function-local member only. There exist no multithreaded access to it realy ...

    @attention      a) To prevent us against wrong using, the default ctor, copy ctor and the =operator are maked private!
                    b) Use interface "IRWLock" of set LockHelper only - because we must support a finer granularity of locking.
                       Interface "IMutex" should be used by easier guard implementations ... like "ResetableGuard"!

    @implements     -
    @base           INonCopyable

    @devstatus      ready to use
*//*-*************************************************************************************************************/
class ReadGuard : private INonCopyable
{
    //-------------------------------------------------------------------------------------------------------------
    //  public methods
    //-------------------------------------------------------------------------------------------------------------
    public:

        /*-****************************************************************************************************//**
            @short      ctor
            @descr      These ctors initialize the guard with a reference to used lock member of object to protect.
                        Null isn't allowed as value!

            @seealso    -

            @param      "pLock" ,reference to used lock member of object to protect
            @param      "rLock" ,reference to used lock member of object to protect
            @return     -

            @onerror    -
        *//*-*****************************************************************************************************/
        inline ReadGuard( IRWLock* pLock )
            :   m_pLock     ( pLock     )
            ,   m_bLocked   ( sal_False )
        {
            lock();
        }

        //*********************************************************************************************************
        inline ReadGuard( IRWLock& rLock )
            :   m_pLock     ( &rLock    )
            ,   m_bLocked   ( sal_False )
        {
            lock();
        }

        /*-****************************************************************************************************//**
            @short      dtor
            @descr      We unlock the used lock member automaticly if user forget it.

            @seealso    -

            @param      -
            @return     -

            @onerror    -
        *//*-*****************************************************************************************************/
        inline ~ReadGuard()
        {
            unlock();
        }

        /*-****************************************************************************************************//**
            @short      set read lock
            @descr      Call this method to set the read lock. The call will block till all current threads are synchronized!

            @seealso    method unlock()

            @param      -
            @return     -

            @onerror    -
        *//*-*****************************************************************************************************/
        inline void lock()
        {
            if( m_bLocked == sal_False )
            {
                m_pLock->acquireReadAccess();
                m_bLocked = sal_True;
            }
        }

        /*-****************************************************************************************************//**
            @short      unset read lock
            @descr      Call this method to unlock the rw-lock temp.!
                        Normaly we do it at dtor automaticly for you ...

            @seealso    method lock()

            @param      -
            @return     -

            @onerror    -
        *//*-*****************************************************************************************************/
        inline void unlock()
        {
            if( m_bLocked == sal_True )
            {
                m_pLock->releaseReadAccess();
                m_bLocked = sal_False;
            }
        }

    //-------------------------------------------------------------------------------------------------------------
    //  private methods
    //-------------------------------------------------------------------------------------------------------------
    private:

        /*-****************************************************************************************************//**
            @short      disable using of these functions!
            @descr      It's not allowed to use this methods. Different problem can occure otherwise.
                        Thats why we disable it by make it private.

            @seealso    other ctor

            @param      -
            @return     -

            @onerror    -
        *//*-*****************************************************************************************************/
        ReadGuard();

    //-------------------------------------------------------------------------------------------------------------
    //  private member
    //-------------------------------------------------------------------------------------------------------------
    private:

        IRWLock*    m_pLock     ;   /// reference to lock-member of protected object
        sal_Bool    m_bLocked   ;   /// protection against multiple lock calls without unlock!

};      //  class ReadGuard

}       //  namespace framework

#endif  //  #ifndef __FRAMEWORK_THREADHELP_READGUARD_HXX_
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2004-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */
#include "fboss/agent/platforms/wedge/WedgeProductInfo.h"
#include "fboss/agent/FbossError.h"
#include <folly/FileUtil.h>
#include <folly/json.h>
#include <folly/dynamic.h>
#include <folly/MacAddress.h>

namespace {
constexpr auto kInfo = "Information";
constexpr auto kSysMfgDate = "System Manufacturing Date";
constexpr auto kSysMfg = "System Manufacturer";
constexpr auto kSysAmbPartNum = "System Assembly Part Number";
constexpr auto kAmbAt = "Assembled At";
constexpr auto kPcbMfg = "PCB Manufacturer";
constexpr auto kProdAssetTag = "Product Asset Tag";
constexpr auto kProdName = "Product Name";
constexpr auto kProdVersion = "Product Version";
constexpr auto kProductionState = "Product Production State";
constexpr auto kProdPartNum = "Product Part Number";
constexpr auto kSerialNum = "Product Serial Number";
constexpr auto kSubVersion = "Product Sub-Version";
constexpr auto kOdmPcbPartNum = "ODM PCB Part Number";
constexpr auto kOdmPcbSerialNum = "ODM PCB Serial Number";
constexpr auto kFbPcbPartNum = "Facebook PCB Part Number";
constexpr auto kExtMacSize = "Extended MAC Address Size";
constexpr auto kExtMacBase = "Extended MAC Base";
constexpr auto kLocalMac = "Local MAC";
constexpr auto kVersion = "Version";
constexpr auto kFabricLocation = "Location on Fabric";
}

namespace facebook { namespace fboss {

using folly::MacAddress;
using folly::StringPiece;
using folly::dynamic;
using folly::parseJson;

WedgeProductInfo::WedgeProductInfo(StringPiece path)
  : path_(path) {
}

void WedgeProductInfo::initialize() {
  try {
    std::string data;
    folly::readFile(path_.str().c_str(), data);
    parse(data);
  } catch (const std::exception& err) {
    LOG(ERROR) << err.what();
    // if fruid info fails fall back to hostname
    initFromHostname();
  }
}

void WedgeProductInfo::getInfo(ProductInfo& info) {
  info = productInfo_;
}

std::string WedgeProductInfo::getFabricLocation() {
  return productInfo_.fabricLocation;
}

std::string WedgeProductInfo::getProductName() {
  return productInfo_.product;
}

void WedgeProductInfo::parse(std::string data) {
  dynamic info = parseJson(data)[kInfo];
  productInfo_.oem = folly::to<std::string>(info[kSysMfg].asString());
  productInfo_.product = folly::to<std::string>(info[kProdName].asString());
  productInfo_.serial = folly::to<std::string>(info[kSerialNum].asString());
  productInfo_.mfgDate = folly::to<std::string>(info[kSysMfgDate].asString());
  productInfo_.systemPartNumber =
                      folly::to<std::string>(info[kSysAmbPartNum].asString());
  productInfo_.assembledAt = folly::to<std::string>(info[kAmbAt].asString());
  productInfo_.pcbManufacturer =
                              folly::to<std::string>(info[kPcbMfg].asString());
  productInfo_.assetTag =
                        folly::to<std::string>(info[kProdAssetTag].asString());
  productInfo_.partNumber =
                          folly::to<std::string>(info[kProdPartNum].asString());
  productInfo_.odmPcbPartNumber = folly::to<std::string>
                                    (info[kOdmPcbPartNum].asString());
  productInfo_.odmPcbSerial = folly::to<std::string>
                                  (info[kOdmPcbSerialNum].asString());
  productInfo_.fbPcbPartNumber = folly::to<std::string>
                                    (info[kFbPcbPartNum].asString());
  productInfo_.fabricLocation = folly::to<std::string>
                                    (info[kFabricLocation].asString());
  productInfo_.version = info[kVersion].asInt();
  productInfo_.subVersion = info[kSubVersion].asInt();
  productInfo_.productionState = info[kProductionState].asInt();
  productInfo_.productVersion = info[kProdVersion].asInt();
  productInfo_.bmcMac = folly::to<std::string>(info[kLocalMac].asString());
  productInfo_.mgmtMac = folly::to<std::string>(info[kExtMacBase].asString());
  auto macBase = MacAddress(info[kExtMacBase].asString()).u64HBO() + 1;
  productInfo_.macRangeStart = MacAddress::fromHBO(macBase).toString();
  productInfo_.macRangeSize = info[kExtMacSize].asInt() - 1;
}

}} // facebook::fboss
<commit_msg>Append L/R to the serial number of the fabric card<commit_after>/*
 *  Copyright (c) 2004-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */
#include "fboss/agent/platforms/wedge/WedgeProductInfo.h"
#include "fboss/agent/FbossError.h"
#include <folly/FileUtil.h>
#include <folly/json.h>
#include <folly/dynamic.h>
#include <folly/MacAddress.h>

namespace {
constexpr auto kInfo = "Information";
constexpr auto kSysMfgDate = "System Manufacturing Date";
constexpr auto kSysMfg = "System Manufacturer";
constexpr auto kSysAmbPartNum = "System Assembly Part Number";
constexpr auto kAmbAt = "Assembled At";
constexpr auto kPcbMfg = "PCB Manufacturer";
constexpr auto kProdAssetTag = "Product Asset Tag";
constexpr auto kProdName = "Product Name";
constexpr auto kProdVersion = "Product Version";
constexpr auto kProductionState = "Product Production State";
constexpr auto kProdPartNum = "Product Part Number";
constexpr auto kSerialNum = "Product Serial Number";
constexpr auto kSubVersion = "Product Sub-Version";
constexpr auto kOdmPcbPartNum = "ODM PCB Part Number";
constexpr auto kOdmPcbSerialNum = "ODM PCB Serial Number";
constexpr auto kFbPcbPartNum = "Facebook PCB Part Number";
constexpr auto kExtMacSize = "Extended MAC Address Size";
constexpr auto kExtMacBase = "Extended MAC Base";
constexpr auto kLocalMac = "Local MAC";
constexpr auto kVersion = "Version";
constexpr auto kFabricLocation = "Location on Fabric";
constexpr auto kFabricLocationLeft = "LEFT";
constexpr auto kFabricLocationRight = "RIGHT";
}

namespace facebook { namespace fboss {

using folly::MacAddress;
using folly::StringPiece;
using folly::dynamic;
using folly::parseJson;

WedgeProductInfo::WedgeProductInfo(StringPiece path)
  : path_(path) {
}

void WedgeProductInfo::initialize() {
  try {
    std::string data;
    folly::readFile(path_.str().c_str(), data);
    parse(data);
  } catch (const std::exception& err) {
    LOG(ERROR) << err.what();
    // if fruid info fails fall back to hostname
    initFromHostname();
  }
}

void WedgeProductInfo::getInfo(ProductInfo& info) {
  info = productInfo_;
}

std::string WedgeProductInfo::getFabricLocation() {
  return productInfo_.fabricLocation;
}

std::string WedgeProductInfo::getProductName() {
  return productInfo_.product;
}

void WedgeProductInfo::parse(std::string data) {
  dynamic info = parseJson(data)[kInfo];
  productInfo_.oem = folly::to<std::string>(info[kSysMfg].asString());
  productInfo_.product = folly::to<std::string>(info[kProdName].asString());
  productInfo_.serial = folly::to<std::string>(info[kSerialNum].asString());
  productInfo_.mfgDate = folly::to<std::string>(info[kSysMfgDate].asString());
  productInfo_.systemPartNumber =
                      folly::to<std::string>(info[kSysAmbPartNum].asString());
  productInfo_.assembledAt = folly::to<std::string>(info[kAmbAt].asString());
  productInfo_.pcbManufacturer =
                              folly::to<std::string>(info[kPcbMfg].asString());
  productInfo_.assetTag =
                        folly::to<std::string>(info[kProdAssetTag].asString());
  productInfo_.partNumber =
                          folly::to<std::string>(info[kProdPartNum].asString());
  productInfo_.odmPcbPartNumber = folly::to<std::string>
                                    (info[kOdmPcbPartNum].asString());
  productInfo_.odmPcbSerial = folly::to<std::string>
                                  (info[kOdmPcbSerialNum].asString());
  productInfo_.fbPcbPartNumber = folly::to<std::string>
                                    (info[kFbPcbPartNum].asString());
  productInfo_.fabricLocation = folly::to<std::string>
                                    (info[kFabricLocation].asString());
  // Append L/R to the serial number based on the fabricLocation
  if (productInfo_.fabricLocation == kFabricLocationLeft) {
    productInfo_.serial = productInfo_.serial + "L";
  } else if (productInfo_.fabricLocation == kFabricLocationRight) {
    productInfo_.serial = productInfo_.serial + "R";
  }
  productInfo_.version = info[kVersion].asInt();
  productInfo_.subVersion = info[kSubVersion].asInt();
  productInfo_.productionState = info[kProductionState].asInt();
  productInfo_.productVersion = info[kProdVersion].asInt();
  productInfo_.bmcMac = folly::to<std::string>(info[kLocalMac].asString());
  productInfo_.mgmtMac = folly::to<std::string>(info[kExtMacBase].asString());
  auto macBase = MacAddress(info[kExtMacBase].asString()).u64HBO() + 1;
  productInfo_.macRangeStart = MacAddress::fromHBO(macBase).toString();
  productInfo_.macRangeSize = info[kExtMacSize].asInt() - 1;
}

}} // facebook::fboss
<|endoftext|>


<commit_before>#include "LPD8806.h"
#include "SPI.h"
#include "pins_arduino.h"

// Arduino library to control LPD8806-based RGB LED Strips
// (c) Adafruit industries
// MIT license

/*****************************************************************************/

// Constructor for bitbanged (software) SPI:
LPD8806::LPD8806(uint16_t n, uint8_t dpin, uint8_t cpin) {
  // Allocate 3 bytes per pixel:
  if(NULL != (pixels = (uint8_t *)malloc(numLEDs * 3))) {
    memset(pixels, 0x80, numLEDs * 3); // Init to RGB 'off' state
    numLEDs      = n;
    hardwareSPI  = false;
    datapin      = dpin;
    datapinmask  = digitalPinToBitMask(dpin);
    dataport     = portOutputRegister(digitalPinToPort(dpin));
    clockpin     = cpin;
    clockpinmask = digitalPinToBitMask(cpin);
    clockport    = portOutputRegister(digitalPinToPort(cpin));
    slowmo       = false;
    pause        = 3;
  }
}

// Constructor for hardware SPI use:
LPD8806::LPD8806(uint16_t n) {
  // Allocate 3 bytes per pixel:
  if(NULL != (pixels = (uint8_t *)malloc(numLEDs * 3))) {
    memset(pixels, 0x80, numLEDs * 3); // Init to RGB 'off' state
    numLEDs     = n;
    hardwareSPI = true;
    pause       = 3;
  }
}

void LPD8806::begin(void) {

  if (hardwareSPI) {
    SPI.begin();
    // Run the SPI bus at 2MHz.  Although the LPD8806 should, in theory,
    // work up to 20MHz, the unshielded wiring from the Arduino is more
    // susceptible to interference.  Experiment and see what you get.
    SPI.setClockDivider(SPI_CLOCK_DIV8);
  } else {
    pinMode(datapin , OUTPUT);
    pinMode(clockpin, OUTPUT);
    if(slowmo) digitalWrite(clockpin, LOW);
    else       *clockport &= ~clockpinmask; // Clock = low
  }

  // Issue initial latch to 'wake up' strip (latch length varies w/numLEDs)
  writezeros(3 * ((numLEDs + 63) / 64));
}

uint16_t LPD8806::numPixels(void) {
  return numLEDs;
}

void LPD8806::writezeros(uint16_t n) {
  if (hardwareSPI) {
    while(n--) SPI.transfer(0);
  } else if(slowmo) {
    digitalWrite(datapin, LOW);
    for(uint16_t i = 8 * n; i>0; i--) {
      digitalWrite(clockpin, HIGH);
      digitalWrite(clockpin, LOW);
    }
  } else {
    *dataport &= ~datapinmask; // Data low
    for(uint16_t i = 8 * n; i>0; i--) {
      *clockport |=  clockpinmask;
      *clockport &= ~clockpinmask;
    }
  }
}

// This is how data is pushed to the strip.  Unfortunately, the company
// that makes the chip didnt release the  protocol document or you need
// to sign an NDA or something stupid like that, but we reverse engineered
// this from a strip controller and it seems to work very nicely!
void LPD8806::show(void) {
  uint16_t i, nl3 = numLEDs * 3; // 3 bytes per LED

  // write 24 bits per pixel
  if (hardwareSPI) {
    for (i=0; i<nl3; i++ ) {
      SPDR = pixels[i];
      while(!(SPSR & (1<<SPIF)));
    }
  } else if(slowmo) {
    for (i=0; i<nl3; i++ ) {
      for (uint8_t bit=0x80; bit; bit >>= 1) {
        if(pixels[i] & bit) digitalWrite(datapin, HIGH);
        else                digitalWrite(datapin, LOW);
        digitalWrite(clockpin, HIGH);
        digitalWrite(clockpin, LOW);
      }
    }
  } else {
    for (i=0; i<nl3; i++ ) {
      for (uint8_t bit=0x80; bit; bit >>= 1) {
        if(pixels[i] & bit) *dataport |=  datapinmask;
        else                *dataport &= ~datapinmask;
        *clockport |=  clockpinmask;
        *clockport &= ~clockpinmask;
      }
    }
  }

  // Write latch at end of data; latch length varies with number of LEDs
  writezeros(3 * ((numLEDs + 63) / 64));

  // We need to have a delay here, a few ms seems to do the job
  // shorter may be OK as well - need to experiment :(
  delay(pause);
}

// Convert R,G,B to combined 32-bit color
uint32_t LPD8806::Color(byte r, byte g, byte b) {
  // Take the lowest 7 bits of each value and append them end to end
  // We have the top bit set high (its a 'parity-like' bit in the protocol
  // and must be set!)
  return 0x808080 | ((uint32_t)g << 16) | ((uint32_t)r << 8) | (uint32_t)b;
}

// store the rgb component in our array
void LPD8806::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
  if (n >= numLEDs) return; // '>=' because arrays are 0-indexed

  pixels[n*3  ] = g | 0x80;
  pixels[n*3+1] = r | 0x80;
  pixels[n*3+2] = b | 0x80;
}

void LPD8806::setPixelColor(uint16_t n, uint32_t c) {
  if (n >= numLEDs) return; // '>=' because arrays are 0-indexed

  pixels[n*3  ] = (c >> 16) | 0x80;
  pixels[n*3+1] = (c >>  8) | 0x80;
  pixels[n*3+2] =  c        | 0x80;
}

<commit_msg>malloc/init bug fixed. Sorry, my bad!<commit_after>#include "LPD8806.h"
#include "SPI.h"
#include "pins_arduino.h"

// Arduino library to control LPD8806-based RGB LED Strips
// (c) Adafruit industries
// MIT license

/*****************************************************************************/

// Constructor for bitbanged (software) SPI:
LPD8806::LPD8806(uint16_t n, uint8_t dpin, uint8_t cpin) {
  // Allocate 3 bytes per pixel:
  if(NULL != (pixels = (uint8_t *)malloc(n * 3))) {
    memset(pixels, 0x80, n * 3); // Init to RGB 'off' state
    numLEDs      = n;
    hardwareSPI  = false;
    datapin      = dpin;
    datapinmask  = digitalPinToBitMask(dpin);
    dataport     = portOutputRegister(digitalPinToPort(dpin));
    clockpin     = cpin;
    clockpinmask = digitalPinToBitMask(cpin);
    clockport    = portOutputRegister(digitalPinToPort(cpin));
    slowmo       = false;
    pause        = 3;
  }
}

// Constructor for hardware SPI use:
LPD8806::LPD8806(uint16_t n) {
  // Allocate 3 bytes per pixel:
  if(NULL != (pixels = (uint8_t *)malloc(n * 3))) {
    memset(pixels, 0x80, n * 3); // Init to RGB 'off' state
    numLEDs     = n;
    hardwareSPI = true;
    pause       = 3;
  }
}

void LPD8806::begin(void) {

  if (hardwareSPI) {
    SPI.begin();
    // Run the SPI bus at 2MHz.  Although the LPD8806 should, in theory,
    // work up to 20MHz, the unshielded wiring from the Arduino is more
    // susceptible to interference.  Experiment and see what you get.
    SPI.setClockDivider(SPI_CLOCK_DIV8);
  } else {
    pinMode(datapin , OUTPUT);
    pinMode(clockpin, OUTPUT);
    if(slowmo) digitalWrite(clockpin, LOW);
    else       *clockport &= ~clockpinmask; // Clock = low
  }

  // Issue initial latch to 'wake up' strip (latch length varies w/numLEDs)
  writezeros(3 * ((numLEDs + 63) / 64));
}

uint16_t LPD8806::numPixels(void) {
  return numLEDs;
}

void LPD8806::writezeros(uint16_t n) {
  if (hardwareSPI) {
    while(n--) SPI.transfer(0);
  } else if(slowmo) {
    digitalWrite(datapin, LOW);
    for(uint16_t i = 8 * n; i>0; i--) {
      digitalWrite(clockpin, HIGH);
      digitalWrite(clockpin, LOW);
    }
  } else {
    *dataport &= ~datapinmask; // Data low
    for(uint16_t i = 8 * n; i>0; i--) {
      *clockport |=  clockpinmask;
      *clockport &= ~clockpinmask;
    }
  }
}

// This is how data is pushed to the strip.  Unfortunately, the company
// that makes the chip didnt release the  protocol document or you need
// to sign an NDA or something stupid like that, but we reverse engineered
// this from a strip controller and it seems to work very nicely!
void LPD8806::show(void) {
  uint16_t i, nl3 = numLEDs * 3; // 3 bytes per LED

  // write 24 bits per pixel
  if (hardwareSPI) {
    for (i=0; i<nl3; i++ ) {
      SPDR = pixels[i];
      while(!(SPSR & (1<<SPIF)));
    }
  } else if(slowmo) {
    for (i=0; i<nl3; i++ ) {
      for (uint8_t bit=0x80; bit; bit >>= 1) {
        if(pixels[i] & bit) digitalWrite(datapin, HIGH);
        else                digitalWrite(datapin, LOW);
        digitalWrite(clockpin, HIGH);
        digitalWrite(clockpin, LOW);
      }
    }
  } else {
    for (i=0; i<nl3; i++ ) {
      for (uint8_t bit=0x80; bit; bit >>= 1) {
        if(pixels[i] & bit) *dataport |=  datapinmask;
        else                *dataport &= ~datapinmask;
        *clockport |=  clockpinmask;
        *clockport &= ~clockpinmask;
      }
    }
  }

  // Write latch at end of data; latch length varies with number of LEDs
  writezeros(3 * ((numLEDs + 63) / 64));

  // We need to have a delay here, a few ms seems to do the job
  // shorter may be OK as well - need to experiment :(
  delay(pause);
}

// Convert R,G,B to combined 32-bit color
uint32_t LPD8806::Color(byte r, byte g, byte b) {
  // Take the lowest 7 bits of each value and append them end to end
  // We have the top bit set high (its a 'parity-like' bit in the protocol
  // and must be set!)
  return 0x808080 | ((uint32_t)g << 16) | ((uint32_t)r << 8) | (uint32_t)b;
}

// store the rgb component in our array
void LPD8806::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
  if (n >= numLEDs) return; // '>=' because arrays are 0-indexed

  pixels[n*3  ] = g | 0x80;
  pixels[n*3+1] = r | 0x80;
  pixels[n*3+2] = b | 0x80;
}

void LPD8806::setPixelColor(uint16_t n, uint32_t c) {
  if (n >= numLEDs) return; // '>=' because arrays are 0-indexed

  pixels[n*3  ] = (c >> 16) | 0x80;
  pixels[n*3+1] = (c >>  8) | 0x80;
  pixels[n*3+2] =  c        | 0x80;
}

<|endoftext|>


<commit_before>/**
 * \file Library.cpp
 * \author Matt Hammerly
 */

#include <string>
#include <iostream>
#include "Library.h"

/**
 * \brief Default constructor
 *
 * Todo: sanitize the db credentials I guess lol
 */
CLibrary::CLibrary()
{
    char connectionString[512];
    snprintf(connectionString, 512, "dbname=%s host=%s user=%s password=%s",
             DBNAME, DBHOST, DBUSER, DBPW);

    mConnection = PQconnectdb(connectionString);

    if (PQstatus(mConnection) == CONNECTION_BAD)
    {
        std::cout << "Failed to connect to the database" << std::endl;
        exit(0);
    }
}

/**
 * \brief Destructor
 *
 * Will close the database connection before exiting
 */
CLibrary::~CLibrary()
{
    PQfinish(mConnection);
}

/**
 * \brief Create the database tables for this application
 *
 * \returns -1 if something goes wrong
 *
 * Todo: Write database schema in this comment
 *       `create trigger IF NOT EXISTS` or similar
 */
int CLibrary::PrepareDatabase()
{
    PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS tracks (\
                id SERIAL NOT NULL PRIMARY KEY,\
                location TEXT NOT NULL,\
                date_added TIMESTAMPTZ NOT NULL DEFAULT NOW(),\
                flag INTEGER NOT NULL DEFAULT 0\
          )");
    PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS playlists (\
                id SERIAL NOT NULL PRIMARY KEY,\
                title TEXT NOT NULL,\
                length INTEGER NOT NULL,\
                flag INTEGER NOT NULL DEFAULT 0\
          )");
    PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS tracks_playlists (\
                id SERIAL NOT NULL PRIMARY KEY,\
                track_id INTEGER NOT NULL,\
                playlist_id INTEGER NOT NULL,\
                position FLOAT NOT NULL\
          )");

    // Create a function to adjust the length of a playlist
    PQexec(mConnection,
            "CREATE OR REPLACE FUNCTION tracks_playlists_insert_func() RETURNS TRIGGER\
            LANGUAGE plpgsql\
            AS $tracks_playlists_insert_func$\
            DECLARE\
                pid INTEGER;\
            BEGIN\
                pid=0;\
                IF (TG_OP = 'INSERT') THEN pid = NEW.playlist_id;\
                ELSIF (TG_OP = 'DELETE') THEN pid = OLD.playlist_id;\
                END IF;\
                WITH Sub AS (SELECT COUNT(id) AS c FROM tracks_playlists WHERE playlist_id = pid)\
                    UPDATE playlists AS Main SET length = Sub.c FROM Sub WHERE Main.id = pid AND 1=1;\
                RETURN NULL;\
            END;\
            $tracks_playlists_insert_func$;");

    // Create a trigger to adjust the length of a playlist on each insert or delete
    PQexec(mConnection,
            "CREATE TRIGGER tracks_playlists_insert_trg\
            AFTER INSERT OR DELETE ON tracks_playlists\
            FOR EACH ROW EXECUTE PROCEDURE tracks_playlists_insert_func();");

    return 0;
}

/**
 * \brief Destroy all database objects used in this application
 *
 * \returns -1 if something goes wrong? I don't know what or why, frankly.
 */
int CLibrary::DestroyDatabase()
{
    PQexec(mConnection, "DROP TRIGGER IF EXISTS tracks_playlists_insert_trg ON tracks_playlists;");
    PQexec(mConnection, "DROP FUNCTION IF EXISTS tracks_playlists_insert_func() CASCADE;");

    PQexec(mConnection, "DROP TABLE IF EXISTS tracks;");
    PQexec(mConnection, "DROP TABLE IF EXISTS playlists;");
    PQexec(mConnection, "DROP TABLE IF EXISTS tracks_playlists;");

    return 0;
}

/**
 * \brief Exposes the database connection status
 * \returns PostgreSQL connection status object
 *
 * Just in case.
 */
ConnStatusType CLibrary::GetStatus()
{
    return PQstatus(mConnection);
}

/**
 * \brief Returns a pointer to the connection object
 *
 * It makes tests easier. Might make this protected and
 * require a testing subclass to use this later, whatever.
 */
PGconn* CLibrary::GetConnection()
{
    return mConnection;
}

/**
 * \brief Add a track to the database
 * \param filepath The filepath of the file to be added
 * \returns The ID of the new track (as a string)
 */
std::string CLibrary::AddTrack(std::string filepath)
{
    std::string query = "INSERT INTO tracks (location) VALUES (";

    // Safety first
    char* escaped_filepath = PQescapeLiteral(mConnection, filepath.c_str(), filepath.length());
    query.append(escaped_filepath);
    PQfreemem(escaped_filepath);

    query.append(") RETURNING id");

    PGresult *res = PQexec(mConnection, query.c_str());
    char* id = PQgetvalue(res, 0, 0);
    return std::string(id);
}
<commit_msg>filepath is a better name than location<commit_after>/**
 * \file Library.cpp
 * \author Matt Hammerly
 */

#include <string>
#include <iostream>
#include "Library.h"

/**
 * \brief Default constructor
 *
 * Todo: sanitize the db credentials I guess lol
 */
CLibrary::CLibrary()
{
    char connectionString[512];
    snprintf(connectionString, 512, "dbname=%s host=%s user=%s password=%s",
             DBNAME, DBHOST, DBUSER, DBPW);

    mConnection = PQconnectdb(connectionString);

    if (PQstatus(mConnection) == CONNECTION_BAD)
    {
        std::cout << "Failed to connect to the database" << std::endl;
        exit(0);
    }
}

/**
 * \brief Destructor
 *
 * Will close the database connection before exiting
 */
CLibrary::~CLibrary()
{
    PQfinish(mConnection);
}

/**
 * \brief Create the database tables for this application
 *
 * \returns -1 if something goes wrong
 *
 * Todo: Write database schema in this comment
 *       `create trigger IF NOT EXISTS` or similar
 */
int CLibrary::PrepareDatabase()
{
    PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS tracks (\
                id SERIAL NOT NULL PRIMARY KEY,\
                filepath TEXT NOT NULL,\
                date_added TIMESTAMPTZ NOT NULL DEFAULT NOW(),\
                flag INTEGER NOT NULL DEFAULT 0\
          )");
    PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS playlists (\
                id SERIAL NOT NULL PRIMARY KEY,\
                title TEXT NOT NULL,\
                length INTEGER NOT NULL,\
                flag INTEGER NOT NULL DEFAULT 0\
          )");
    PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS tracks_playlists (\
                id SERIAL NOT NULL PRIMARY KEY,\
                track_id INTEGER NOT NULL,\
                playlist_id INTEGER NOT NULL,\
                position FLOAT NOT NULL\
          )");

    // Create a function to adjust the length of a playlist
    PQexec(mConnection,
            "CREATE OR REPLACE FUNCTION tracks_playlists_insert_func() RETURNS TRIGGER\
            LANGUAGE plpgsql\
            AS $tracks_playlists_insert_func$\
            DECLARE\
                pid INTEGER;\
            BEGIN\
                pid=0;\
                IF (TG_OP = 'INSERT') THEN pid = NEW.playlist_id;\
                ELSIF (TG_OP = 'DELETE') THEN pid = OLD.playlist_id;\
                END IF;\
                WITH Sub AS (SELECT COUNT(id) AS c FROM tracks_playlists WHERE playlist_id = pid)\
                    UPDATE playlists AS Main SET length = Sub.c FROM Sub WHERE Main.id = pid AND 1=1;\
                RETURN NULL;\
            END;\
            $tracks_playlists_insert_func$;");

    // Create a trigger to adjust the length of a playlist on each insert or delete
    PQexec(mConnection,
            "CREATE TRIGGER tracks_playlists_insert_trg\
            AFTER INSERT OR DELETE ON tracks_playlists\
            FOR EACH ROW EXECUTE PROCEDURE tracks_playlists_insert_func();");

    return 0;
}

/**
 * \brief Destroy all database objects used in this application
 *
 * \returns -1 if something goes wrong? I don't know what or why, frankly.
 */
int CLibrary::DestroyDatabase()
{
    PQexec(mConnection, "DROP TRIGGER IF EXISTS tracks_playlists_insert_trg ON tracks_playlists;");
    PQexec(mConnection, "DROP FUNCTION IF EXISTS tracks_playlists_insert_func() CASCADE;");

    PQexec(mConnection, "DROP TABLE IF EXISTS tracks;");
    PQexec(mConnection, "DROP TABLE IF EXISTS playlists;");
    PQexec(mConnection, "DROP TABLE IF EXISTS tracks_playlists;");

    return 0;
}

/**
 * \brief Exposes the database connection status
 * \returns PostgreSQL connection status object
 *
 * Just in case.
 */
ConnStatusType CLibrary::GetStatus()
{
    return PQstatus(mConnection);
}

/**
 * \brief Returns a pointer to the connection object
 *
 * It makes tests easier. Might make this protected and
 * require a testing subclass to use this later, whatever.
 */
PGconn* CLibrary::GetConnection()
{
    return mConnection;
}

/**
 * \brief Add a track to the database
 * \param filepath The filepath of the file to be added
 * \returns The ID of the new track (as a string)
 */
std::string CLibrary::AddTrack(std::string filepath)
{
    std::string query = "INSERT INTO tracks (filepath) VALUES (";

    // Safety first
    char* escaped_filepath = PQescapeLiteral(mConnection, filepath.c_str(), filepath.length());
    query.append(escaped_filepath);
    PQfreemem(escaped_filepath);

    query.append(") RETURNING id");

    PGresult *res = PQexec(mConnection, query.c_str());
    char* id = PQgetvalue(res, 0, 0);
    return std::string(id);
}
<|endoftext|>