<commit_before>// This is based on Rainer Lienhart contribution. Below is the original copyright:
//
/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                University of Augsburg License Agreement
//                For Open Source MultiMedia Computing (MMC) Library
//
// Copyright (C) 2007, University of Augsburg, Germany, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's 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.
//
//   * The name of University of Augsburg, Germany may not 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 University of Augsburg, Germany 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.
//
//M*/

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
//   Author:    Rainer Lienhart
//              email: Rainer.Lienhart@informatik.uni-augsburg.de
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

// Please cite the following two papers:
// 1. Shechtman, E., Irani, M.:
//		Matching local self-similarities across images and videos.
// 		CVPR, (2007)
// 2. Eva Horster, Thomas Greif, Rainer Lienhart, Malcolm Slaney.
//		Comparing Local Feature Descriptors in pLSA-Based Image Models.
//		30th Annual Symposium of the German Association for
//      Pattern Recognition (DAGM) 2008, Munich, Germany, June 2008.

#include "precomp.hpp"

namespace cv
{

SelfSimDescriptor::SelfSimDescriptor()
{
    smallSize = DEFAULT_SMALL_SIZE;
    largeSize = DEFAULT_LARGE_SIZE;
    numberOfAngles = DEFAULT_NUM_ANGLES;
    startDistanceBucket = DEFAULT_START_DISTANCE_BUCKET;
    numberOfDistanceBuckets = DEFAULT_NUM_DISTANCE_BUCKETS;
}

SelfSimDescriptor::SelfSimDescriptor(int _ssize, int _lsize,
                                     int _startDistanceBucket,
                                     int _numberOfDistanceBuckets, int _numberOfAngles)
{
    smallSize = _ssize;
    largeSize = _lsize;
    startDistanceBucket = _startDistanceBucket;
    numberOfDistanceBuckets = _numberOfDistanceBuckets;
    numberOfAngles = _numberOfAngles;
}

SelfSimDescriptor::SelfSimDescriptor(const SelfSimDescriptor& ss)
{
    smallSize = ss.smallSize;
    largeSize = ss.largeSize;
    startDistanceBucket = ss.startDistanceBucket;
    numberOfDistanceBuckets = ss.numberOfDistanceBuckets;
    numberOfAngles = ss.numberOfAngles;
}

SelfSimDescriptor::~SelfSimDescriptor()
{
}

SelfSimDescriptor& SelfSimDescriptor::operator = (const SelfSimDescriptor& ss)
{
    if( this != &ss )
    {
        smallSize = ss.smallSize;
        largeSize = ss.largeSize;
        startDistanceBucket = ss.startDistanceBucket;
        numberOfDistanceBuckets = ss.numberOfDistanceBuckets;
        numberOfAngles = ss.numberOfAngles;
    }
    return *this;
}

size_t SelfSimDescriptor::getDescriptorSize() const
{
    return numberOfAngles*(numberOfDistanceBuckets - startDistanceBucket);
}

Size SelfSimDescriptor::getGridSize( Size imgSize, Size winStride ) const
{
    winStride.width = std::max(winStride.width, 1);
    winStride.height = std::max(winStride.height, 1);
    int border = largeSize/2 + smallSize/2;
    return Size(std::max(imgSize.width - border*2 + winStride.width - 1, 0)/winStride.width,
                std::max(imgSize.height - border*2 + winStride.height - 1, 0)/winStride.height);
}

// TODO: optimized with SSE2
void SelfSimDescriptor::SSD(const Mat& img, Point pt, Mat& ssd) const
{
	int x, y, dx, dy, r0 = largeSize/2, r1 = smallSize/2;
    int step = img.step;
    for( y = -r0; y <= r0; y++ )
    {
        float* sptr = ssd.ptr<float>(y+r0) + r0;
        for( x = -r0; x <= r0; x++ )
        {
            int sum = 0;
            const uchar* src0 = img.ptr<uchar>(y + pt.y - r1) + x + pt.x;
            const uchar* src1 = img.ptr<uchar>(pt.y - r1) + pt.x;
            for( dy = -r1; dy <= r1; dy++, src0 += step, src1 += step )
                for( dx = -r1; dx <= r1; dx++ )
                {
                    int t = src0[dx] - src1[dx];
                    sum += t*t;
                }
            sptr[x] = (float)sum;
        }
    }
}

void SelfSimDescriptor::compute(const Mat& img, vector<float>& descriptors, Size winStride,
                                const vector<Point>& locations) const
{
    CV_Assert( img.depth() == CV_8U );

    winStride.width = std::max(winStride.width, 1);
    winStride.height = std::max(winStride.height, 1);
    Size gridSize = getGridSize(img.size(), winStride);
    int i, nwindows = locations.empty() ? gridSize.width*gridSize.height : (int)locations.size();
    int border = largeSize/2 + smallSize/2;
    int fsize = (int)getDescriptorSize();
    vector<float> tempFeature(fsize+1);
    descriptors.resize(fsize*nwindows + 1);
    Mat ssd(largeSize, largeSize, CV_32F), mappingMask;
    computeLogPolarMapping(mappingMask);

#if 0 //def _OPENMP
    int nthreads = cvGetNumThreads();
    #pragma omp parallel for num_threads(nthreads)
#endif
    for( i = 0; i < nwindows; i++ )
    {
        Point pt;
        float* feature0 = &descriptors[fsize*i];
        float* feature = &tempFeature[0];
        int x, y, j;

        if( !locations.empty() )
        {
            pt = locations[i];
            if( pt.x < border || pt.x >= img.cols - border ||
                pt.y < border || pt.y >= img.rows - border )
            {
                for( j = 0; j < fsize; j++ )
                    feature0[j] = 0.f;
                continue;
            }
        }
        else
            pt = Point((i % gridSize.width)*winStride.width + border,
                       (i / gridSize.width)*winStride.height + border);

        SSD(img, pt, ssd);

	    // Determine in the local neighborhood the largest difference and use for normalization
	    float var_noise = 1000.f;
        for( y = -1; y <= 1 ; y++ )
		    for( x = -1 ; x <= 1 ; x++ )
                var_noise = std::max(var_noise, ssd.at<float>(largeSize/2+y, largeSize/2+x));

        for( j = 0; j <= fsize; j++ )
            feature[j] = FLT_MAX;

	    // Derive feature vector before exp(-x) computation
	    // Idea: for all  x,a >= 0, a=const.   we have:
	    //       max [ exp( -x / a) ] = exp ( -min(x) / a )
	    // Thus, determine min(ssd) and store in feature[...]
    	for( y = 0; y < ssd.rows; y++ )
        {
		    const schar *mappingMaskPtr = mappingMask.ptr<schar>(y);
		    const float *ssdPtr = ssd.ptr<float>(y);
		    for( x = 0 ; x < ssd.cols; x++ )
            {
                int index = mappingMaskPtr[x];
                feature[index] = std::max(feature[index], ssdPtr[x]);
		    }
	    }

        var_noise = -1.f/var_noise;
    	for( j = 0; j < fsize; j++ )
            feature0[j] = feature[j]*var_noise;
        Mat _f(1, fsize, CV_32F, feature0);
        cv::exp(_f, _f);
    }
}

void SelfSimDescriptor::computeLogPolarMapping(Mat& mappingMask) const
{
    mappingMask.create(largeSize, largeSize, CV_8S);

    // What we want is
    //		 log_m (radius) = numberOfDistanceBuckets
    //	<==> log_10 (radius) / log_10 (m) = numberOfDistanceBuckets
    //	<==> log_10 (radius) / numberOfDistanceBuckets = log_10 (m)
    //	<==> m = 10 ^ log_10(m) = 10 ^ [log_10 (radius) / numberOfDistanceBuckets]
    //
    int radius = largeSize/2, angleBucketSize = 360 / numberOfAngles;
    int fsize = (int)getDescriptorSize();
    double inv_log10m = (double)numberOfDistanceBuckets/log10((double)radius);

    for (int y=-radius ; y<=radius ; y++)
    {
        schar* mrow = mappingMask.ptr<schar>(y+radius);
        for (int x=-radius ; x<=radius ; x++)
        {
            int index = fsize;
            float dist = (float)std::sqrt((float)x*x + (float)y*y);
            int distNo = dist > 0 ? cvRound(log10(dist)*inv_log10m) : 0;
            if( startDistanceBucket <= distNo && distNo < numberOfDistanceBuckets )
            {
                float angle = std::atan2( (float)y, (float)x ) / (float)CV_PI * 180.0f;
                if (angle < 0) angle += 360.0f;
                int angleInt = (cvRound(angle) + angleBucketSize/2) % 360;
                int angleIndex = angleInt / angleBucketSize;
                index = (distNo-startDistanceBucket)*numberOfAngles + angleIndex;
            }
            mrow[x + radius] = saturate_cast<schar>(index);
        }
    }
}

}
<commit_msg>fixed computation of feature vectors in self-similarity descriptor (max->min)<commit_after>// This is based on Rainer Lienhart contribution. Below is the original copyright:
//
/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                University of Augsburg License Agreement
//                For Open Source MultiMedia Computing (MMC) Library
//
// Copyright (C) 2007, University of Augsburg, Germany, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's 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.
//
//   * The name of University of Augsburg, Germany may not 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 University of Augsburg, Germany 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.
//
//M*/

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
//   Author:    Rainer Lienhart
//              email: Rainer.Lienhart@informatik.uni-augsburg.de
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

// Please cite the following two papers:
// 1. Shechtman, E., Irani, M.:
//		Matching local self-similarities across images and videos.
// 		CVPR, (2007)
// 2. Eva Horster, Thomas Greif, Rainer Lienhart, Malcolm Slaney.
//		Comparing Local Feature Descriptors in pLSA-Based Image Models.
//		30th Annual Symposium of the German Association for
//      Pattern Recognition (DAGM) 2008, Munich, Germany, June 2008.

#include "precomp.hpp"

namespace cv
{

SelfSimDescriptor::SelfSimDescriptor()
{
    smallSize = DEFAULT_SMALL_SIZE;
    largeSize = DEFAULT_LARGE_SIZE;
    numberOfAngles = DEFAULT_NUM_ANGLES;
    startDistanceBucket = DEFAULT_START_DISTANCE_BUCKET;
    numberOfDistanceBuckets = DEFAULT_NUM_DISTANCE_BUCKETS;
}

SelfSimDescriptor::SelfSimDescriptor(int _ssize, int _lsize,
                                     int _startDistanceBucket,
                                     int _numberOfDistanceBuckets, int _numberOfAngles)
{
    smallSize = _ssize;
    largeSize = _lsize;
    startDistanceBucket = _startDistanceBucket;
    numberOfDistanceBuckets = _numberOfDistanceBuckets;
    numberOfAngles = _numberOfAngles;
}

SelfSimDescriptor::SelfSimDescriptor(const SelfSimDescriptor& ss)
{
    smallSize = ss.smallSize;
    largeSize = ss.largeSize;
    startDistanceBucket = ss.startDistanceBucket;
    numberOfDistanceBuckets = ss.numberOfDistanceBuckets;
    numberOfAngles = ss.numberOfAngles;
}

SelfSimDescriptor::~SelfSimDescriptor()
{
}

SelfSimDescriptor& SelfSimDescriptor::operator = (const SelfSimDescriptor& ss)
{
    if( this != &ss )
    {
        smallSize = ss.smallSize;
        largeSize = ss.largeSize;
        startDistanceBucket = ss.startDistanceBucket;
        numberOfDistanceBuckets = ss.numberOfDistanceBuckets;
        numberOfAngles = ss.numberOfAngles;
    }
    return *this;
}

size_t SelfSimDescriptor::getDescriptorSize() const
{
    return numberOfAngles*(numberOfDistanceBuckets - startDistanceBucket);
}

Size SelfSimDescriptor::getGridSize( Size imgSize, Size winStride ) const
{
    winStride.width = std::max(winStride.width, 1);
    winStride.height = std::max(winStride.height, 1);
    int border = largeSize/2 + smallSize/2;
    return Size(std::max(imgSize.width - border*2 + winStride.width - 1, 0)/winStride.width,
                std::max(imgSize.height - border*2 + winStride.height - 1, 0)/winStride.height);
}

// TODO: optimized with SSE2
void SelfSimDescriptor::SSD(const Mat& img, Point pt, Mat& ssd) const
{
	int x, y, dx, dy, r0 = largeSize/2, r1 = smallSize/2;
    int step = img.step;
    for( y = -r0; y <= r0; y++ )
    {
        float* sptr = ssd.ptr<float>(y+r0) + r0;
        for( x = -r0; x <= r0; x++ )
        {
            int sum = 0;
            const uchar* src0 = img.ptr<uchar>(y + pt.y - r1) + x + pt.x;
            const uchar* src1 = img.ptr<uchar>(pt.y - r1) + pt.x;
            for( dy = -r1; dy <= r1; dy++, src0 += step, src1 += step )
                for( dx = -r1; dx <= r1; dx++ )
                {
                    int t = src0[dx] - src1[dx];
                    sum += t*t;
                }
            sptr[x] = (float)sum;
        }
    }
}

void SelfSimDescriptor::compute(const Mat& img, vector<float>& descriptors, Size winStride,
                                const vector<Point>& locations) const
{
    CV_Assert( img.depth() == CV_8U );

    winStride.width = std::max(winStride.width, 1);
    winStride.height = std::max(winStride.height, 1);
    Size gridSize = getGridSize(img.size(), winStride);
    int i, nwindows = locations.empty() ? gridSize.width*gridSize.height : (int)locations.size();
    int border = largeSize/2 + smallSize/2;
    int fsize = (int)getDescriptorSize();
    vector<float> tempFeature(fsize+1);
    descriptors.resize(fsize*nwindows + 1);
    Mat ssd(largeSize, largeSize, CV_32F), mappingMask;
    computeLogPolarMapping(mappingMask);

#if 0 //def _OPENMP
    int nthreads = cvGetNumThreads();
    #pragma omp parallel for num_threads(nthreads)
#endif
    for( i = 0; i < nwindows; i++ )
    {
        Point pt;
        float* feature0 = &descriptors[fsize*i];
        float* feature = &tempFeature[0];
        int x, y, j;

        if( !locations.empty() )
        {
            pt = locations[i];
            if( pt.x < border || pt.x >= img.cols - border ||
                pt.y < border || pt.y >= img.rows - border )
            {
                for( j = 0; j < fsize; j++ )
                    feature0[j] = 0.f;
                continue;
            }
        }
        else
            pt = Point((i % gridSize.width)*winStride.width + border,
                       (i / gridSize.width)*winStride.height + border);

        SSD(img, pt, ssd);

	    // Determine in the local neighborhood the largest difference and use for normalization
	    float var_noise = 1000.f;
        for( y = -1; y <= 1 ; y++ )
		    for( x = -1 ; x <= 1 ; x++ )
                var_noise = std::max(var_noise, ssd.at<float>(largeSize/2+y, largeSize/2+x));

        for( j = 0; j <= fsize; j++ )
            feature[j] = FLT_MAX;

	    // Derive feature vector before exp(-x) computation
	    // Idea: for all  x,a >= 0, a=const.   we have:
	    //       max [ exp( -x / a) ] = exp ( -min(x) / a )
	    // Thus, determine min(ssd) and store in feature[...]
    	for( y = 0; y < ssd.rows; y++ )
        {
		    const schar *mappingMaskPtr = mappingMask.ptr<schar>(y);
		    const float *ssdPtr = ssd.ptr<float>(y);
		    for( x = 0 ; x < ssd.cols; x++ )
            {
                int index = mappingMaskPtr[x];
                feature[index] = std::min(feature[index], ssdPtr[x]);
		    }
	    }

        var_noise = -1.f/var_noise;
    	for( j = 0; j < fsize; j++ )
            feature0[j] = feature[j]*var_noise;
        Mat _f(1, fsize, CV_32F, feature0);
        cv::exp(_f, _f);
    }
}

void SelfSimDescriptor::computeLogPolarMapping(Mat& mappingMask) const
{
    mappingMask.create(largeSize, largeSize, CV_8S);

    // What we want is
    //		 log_m (radius) = numberOfDistanceBuckets
    //	<==> log_10 (radius) / log_10 (m) = numberOfDistanceBuckets
    //	<==> log_10 (radius) / numberOfDistanceBuckets = log_10 (m)
    //	<==> m = 10 ^ log_10(m) = 10 ^ [log_10 (radius) / numberOfDistanceBuckets]
    //
    int radius = largeSize/2, angleBucketSize = 360 / numberOfAngles;
    int fsize = (int)getDescriptorSize();
    double inv_log10m = (double)numberOfDistanceBuckets/log10((double)radius);

    for (int y=-radius ; y<=radius ; y++)
    {
        schar* mrow = mappingMask.ptr<schar>(y+radius);
        for (int x=-radius ; x<=radius ; x++)
        {
            int index = fsize;
            float dist = (float)std::sqrt((float)x*x + (float)y*y);
            int distNo = dist > 0 ? cvRound(log10(dist)*inv_log10m) : 0;
            if( startDistanceBucket <= distNo && distNo < numberOfDistanceBuckets )
            {
                float angle = std::atan2( (float)y, (float)x ) / (float)CV_PI * 180.0f;
                if (angle < 0) angle += 360.0f;
                int angleInt = (cvRound(angle) + angleBucketSize/2) % 360;
                int angleIndex = angleInt / angleBucketSize;
                index = (distNo-startDistanceBucket)*numberOfAngles + angleIndex;
            }
            mrow[x + radius] = saturate_cast<schar>(index);
        }
    }
}

}
<|endoftext|>


<commit_before>/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Heonyang Ju
*
* 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.
*
*
* MIT ̼ (MIT)
*
* ۱(C) 2015
*
*   , Ʒ      Ʈ ٷ
* Ʈ 纻 õ ("Ʈ") ȹϴ
*   ȴ.   , , , , , ,  ̼
* ׸/Ǵ Ʈ 纻 Ǹſ ޵ Ʈ
* ׷   ֵ 㰡ϴ ѿ    Ѵ.
*
*  ۱ ǥÿ  㰡 ǥô  Ʈ 纻 Ǵ  Ϻκп
* ԵǾ Ѵ.
*
*  Ʈ Ư  ħ   , ռ
* ѵǴ  ƴ  Ǵ     "ִ ״"
*  ȴ.  쿡  Ǵ ۱ڴ Ʈ  Ȱ
* Ǵ Ʈ  Ǵ ŷκ ߻ϴ , ҹ  Ǵ Ÿ ࿡
* ,  Ҽ,  Ǵ ٸ å ؼ å .
*
*  MIT̼  ̸,  ؼ  Ѵ.
*/

/*
* filename	DisplayObjectContainer.cpp
* fileinfo	ȭ ǥ ü ̳ Ŭ
* author	 (Heonyang Ju)
*/
#include "InteractiveObject.h"
#include "DisplayObjectContainer.h"

COMA_USING_NS

DisplayObjectContainer::DisplayObjectContainer()
{
}

DisplayObjectContainer::~DisplayObjectContainer()
{
	for (unsigned int i = 0; i < objectList.size(); i++)
	{
		objectList[i]->_unregisterParent();
	}
}

void DisplayObjectContainer::addChild(DisplayObject* object)
{
	if (object->getParent())
		return;
	for (unsigned int i = 0; i < objectList.size(); i++)
		if (objectList[i] == object) return;
	object->_registerParent(this);
	objectList.push_back(object);
	_resetSize();
}
void DisplayObjectContainer::removeChild(DisplayObject* object)
{
	for (unsigned int i = 0; i < objectList.size(); i++)
	{
		if (objectList[i] == object)
		{
			object->_unregisterParent();
			objectList.erase(objectList.begin() + i);
			_resetSize();
			return;
		}
	}
}

void DisplayObjectContainer::_resetSize()
{
	Rect rect{ 0, 0, 0, 0 };
	for (unsigned int i = 0; i < objectList.size(); i++)
	{
		if (objectList[i]->getX() < rect.left)
			rect.left = objectList[i]->getX();
		if (objectList[i]->getY() < rect.top)
			rect.top = objectList[i]->getY();
		if (objectList[i]->getWidth() + objectList[i]->getX() > rect.right)
			rect.right = objectList[i]->getWidth() + objectList[i]->getX();
		if (objectList[i]->getHeight() + objectList[i]->getY() > rect.bottom)
			rect.bottom = objectList[i]->getHeight() + objectList[i]->getY();
	}
	setLocalSize(rect.bottom - rect.top, rect.right - rect.left);
}

void DisplayObjectContainer::render(ID2D1HwndRenderTarget* renderTarget, double deltaTime)
{
	DisplayObject::render(renderTarget, deltaTime);
	if (!isVisible())
		return;
	for (unsigned int i = 0; i < objectList.size(); i++)
	{
		objectList[i]->render(renderTarget, deltaTime);
	}
}<commit_msg>컨테이너 크기를 자동으로 조절할 때, 회전량을 고려하도록 설정<commit_after>/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Heonyang Ju
*
* 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.
*
*
* MIT ̼ (MIT)
*
* ۱(C) 2015
*
*   , Ʒ      Ʈ ٷ
* Ʈ 纻 õ ("Ʈ") ȹϴ
*   ȴ.   , , , , , ,  ̼
* ׸/Ǵ Ʈ 纻 Ǹſ ޵ Ʈ
* ׷   ֵ 㰡ϴ ѿ    Ѵ.
*
*  ۱ ǥÿ  㰡 ǥô  Ʈ 纻 Ǵ  Ϻκп
* ԵǾ Ѵ.
*
*  Ʈ Ư  ħ   , ռ
* ѵǴ  ƴ  Ǵ     "ִ ״"
*  ȴ.  쿡  Ǵ ۱ڴ Ʈ  Ȱ
* Ǵ Ʈ  Ǵ ŷκ ߻ϴ , ҹ  Ǵ Ÿ ࿡
* ,  Ҽ,  Ǵ ٸ å ؼ å .
*
*  MIT̼  ̸,  ؼ  Ѵ.
*/

/*
* filename	DisplayObjectContainer.cpp
* fileinfo	ȭ ǥ ü ̳ Ŭ
* author	 (Heonyang Ju)
*/
#include "InteractiveObject.h"
#include "DisplayObjectContainer.h"

COMA_USING_NS

DisplayObjectContainer::DisplayObjectContainer()
{
}

DisplayObjectContainer::~DisplayObjectContainer()
{
	for (unsigned int i = 0; i < objectList.size(); i++)
	{
		objectList[i]->_unregisterParent();
	}
}

void DisplayObjectContainer::addChild(DisplayObject* object)
{
	if (object->getParent())
		return;
	for (unsigned int i = 0; i < objectList.size(); i++)
		if (objectList[i] == object) return;
	object->_registerParent(this);
	objectList.push_back(object);
	_resetSize();
}
void DisplayObjectContainer::removeChild(DisplayObject* object)
{
	for (unsigned int i = 0; i < objectList.size(); i++)
	{
		if (objectList[i] == object)
		{
			object->_unregisterParent();
			objectList.erase(objectList.begin() + i);
			_resetSize();
			return;
		}
	}
}

void DisplayObjectContainer::_resetSize()
{
	Rect rect{ 0, 0, 0, 0 };
	for (unsigned int i = 0; i < objectList.size(); i++)
	{
		Matrix3x2 matrix = objectList[i]->getMatrix() * objectList[i]->getCameraMatrix();

		Point point[4] = {
			matrix.TransformPoint(Point{ 0, 0 }),
			matrix.TransformPoint(Point{ objectList[i]->getLocalSize().width, 0 }),
			matrix.TransformPoint(Point{ objectList[i]->getLocalSize().width, objectList[i]->getLocalSize().height }),
			matrix.TransformPoint(Point{ 0, objectList[i]->getLocalSize().height }) };

		for (int i = 0; i < 4; i++)
		{
			if (point[i].x < rect.left)
				rect.left = point[i].x;
			if (point[i].y < rect.top)
				rect.top = point[i].y;
			if (point[i].x > rect.right)
				rect.right = point[i].x;
			if (point[i].y > rect.bottom)
				rect.bottom = point[i].y;
		}
	}
	setLocalSize(rect.bottom - rect.top, rect.right - rect.left);
}

void DisplayObjectContainer::render(ID2D1HwndRenderTarget* renderTarget, double deltaTime)
{
	DisplayObject::render(renderTarget, deltaTime);
	if (!isVisible())
		return;
	for (unsigned int i = 0; i < objectList.size(); i++)
	{
		objectList[i]->render(renderTarget, deltaTime);
	}
}<|endoftext|>


<commit_before>#pragma once

#include <config.h>

#include "Logging.hpp"
#include "NonCopyable.hpp"
#include "PageManager.hpp"

#include <atomic>
#include <cstddef>
#include <iterator>

namespace tell {
namespace store {

/**
 * @brief A single entry in the log able to store any arbitrary data
 */
class LogEntry : NonCopyable, NonMovable {
public:
    /**
     * @brief Returns the LogEntry struct associated with a given data pointer
     *
     * The data pointer has to be the pointer that was obtained from LogEntry::data().
     *
     * @param data The data pointer
     * @return The LogEntry struct associated with the data pointer
     */
    static LogEntry* entryFromData(char* data) {
        return reinterpret_cast<LogEntry*>(data - sizeof(LogEntry));
    }

    /**
     * @brief Constructor will never be called
     *
     * Everything will be zero initialized.
     */
    LogEntry() = delete;

    /**
     * @brief Destructor will never be called
     */
    ~LogEntry() = delete;

    /**
     * @brief The size of the entry in the log
     *
     * @note This is not the size of the pure data payload but the size of the whole (8 byte padded) log entry.
     */
    uint32_t size() const {
        return (mSize.load() & (0xFFFFFFFFu << 1));
    }

    char* data() {
        return reinterpret_cast<char*>(this) + sizeof(LogEntry);
    }

    const char* data() const {
        return reinterpret_cast<const char*>(this) + sizeof(LogEntry);
    }

    /**
     * @brief Whether the entry was written completely
     *
     * Checks if the LSB of size is 0.
     */
    bool sealed() const {
        return ((mSize.load() & 0x1u) == 0);
    }

    /**
     * @brief Seals the entry indicating that it has been completely written
     *
     * Sets the LSB of size to 0.
     */
    void seal() {
        LOG_ASSERT(!sealed(), "Entry is already sealed");
        mSize.fetch_and(0xFFFFFFFFu << 1);
    }

private:
    friend class LogPage;

    /**
     * @brief Tries to create a new LogEntry at this log position
     *
     * Acquires the space for this entry in the log by compare and swapping the size field from 0 to the given size.
     * Because the PageManager returns zeroed pages, the object is zero initalized (and size thus 0) if nobody has
     * written to it yet.
     *
     * @param size Size of the whole 8 byte padded log entry
     * @return 0 if the log entry was successfully acquired else the previously reserved size of this log entry
     */
    uint32_t tryAcquire(uint32_t size);

    /// Size of this LogEntry as the 8 byte aligned size of the LogEntry class plus data payload
    std::atomic<uint32_t> mSize;
};

/**
 * @brief A single page in the log containing multiple LogEntry's
 *
 * A Log-Page has the following form:
 *
 * -------------------------------------------------------------------------------
 * | next (8 bytes) | offset (4 bytes) | padding (4 bytes) | entry | entry | ... |
 * -------------------------------------------------------------------------------
 */
class LogPage : NonCopyable, NonMovable {
public:
    /// Size of the LogPage data structure
    static constexpr size_t LOG_HEADER_SIZE = 16;

    /// Maximum size of a log entry
    static constexpr uint32_t MAX_ENTRY_SIZE = TELL_PAGE_SIZE - LogPage::LOG_HEADER_SIZE;

    /// Maximum size of a log entries data payload
    static constexpr uint32_t MAX_DATA_SIZE = MAX_ENTRY_SIZE - sizeof(LogEntry);

    /**
     * @brief Iterator for iterating over all entries in a page
     */
    class EntryIterator : public std::iterator<std::input_iterator_tag, LogEntry> {
    public:
        EntryIterator(LogPage* page, uint32_t pos)
                : mPage(page),
                  mPos(pos) {
        }

        LogPage* page() const {
            return mPage;
        }

        uint32_t offset() const {
            return mPos;
        }

        EntryIterator& operator++() {
            auto entry = reinterpret_cast<LogEntry*>(mPage->data() + mPos);
            mPos += entry->size();
            return *this;
        }

        EntryIterator operator++(int) {
            EntryIterator result(*this);
            operator++();
            return result;
        }

        bool operator==(const EntryIterator& rhs) const {
            return ((mPage == rhs.mPage) && (mPos == rhs.mPos));
        }

        bool operator!=(const EntryIterator& rhs) const {
            return !operator==(rhs);
        }

        reference operator*() {
            return *operator->();
        }

        pointer operator->() const {
            return reinterpret_cast<LogEntry*>(mPage->data() + mPos);
        }

    private:
        /// Page this iterator belongs to
        LogPage* mPage;

        /// Current offset the iterator is pointing to
        uint32_t mPos;
    };

    LogPage()
            : mOffset(0x1u) {
    }

    char* data() {
        return reinterpret_cast<char*>(this) + LOG_HEADER_SIZE;
    }

    const char* data() const {
        return reinterpret_cast<const char*>(this) + LOG_HEADER_SIZE;
    }

    /**
     * @brief Appends a new entry to this log page
     *
     * @param size Size of the new entry
     * @return Pointer to allocated LogEntry or nullptr if unable to allocate the entry in this page
     */
    LogEntry* append(uint32_t size);

    /**
     * @brief Page preceeding the current page in the log
     */
    std::atomic<LogPage*>& next() {
        return mNext;
    }

    const std::atomic<LogPage*>& next() const {
        return mNext;
    }

    /**
     * @brief Current offset into the page
     */
    uint32_t offset() const {
        return (mOffset.load() & (0xFFFFFFFFu << 1));
    }

    EntryIterator begin() {
        return EntryIterator(this, 0x0u);
    }

    EntryIterator end() {
        return EntryIterator(this, offset());
    }

    /**
     * @brief Whether the page is sealed and as such write protected
     *
     * Checks if the LSB of offset is 0.
     */
    bool sealed() const {
        return ((mOffset.load() & 0x1u) == 0);
    }

    /**
     * @brief Seals the page indicating that it is write protected
     *
     * Sets the LSB of offset to 0.
     */
    void seal() {
        LOG_ASSERT(!sealed(), "Page is already sealed");
        mOffset.fetch_and(0xFFFFFFFFu << 1);
    }

    /**
     * @brief Tries to seal the page at the given offset
     *
     * Only seals the page if the current page offset is equal to the given offset.
     *
     * Compare and swaps the given offset with LSB 1 (not sealed) to the given offset with LSB 0 (sealed).
     *
     * @param offset The offset this page should be sealed at
     * @return Whether the page was successfully sealed
     */
    bool seal(uint32_t offset) {
        LOG_ASSERT(!sealed(), "Page is already sealed");
        LOG_ASSERT((offset & 0x1u) == 0, "LSB has to be zero");
        auto exp = (offset | 0x1u);
        return mOffset.compare_exchange_strong(exp, offset);
    }

private:
    template <class Impl> friend class Log;

    /**
     * @brief Appends a new entry to the log
     *
     * @param size Complete 8 byte padded size of the new entry
     * @return Pointer to allocated LogEntry or nullptr if unable to allocate the entry in this page
     */
    LogEntry* appendEntry(uint32_t size);

    std::atomic<LogPage*> mNext;
    std::atomic<uint32_t> mOffset;
};

/**
 * @brief Base class for Log implementations
 *
 * Provides the basic page handling mechanism and the head pointer.
 */
class BaseLogImpl {
public:
    LogPage* head() {
        return mHead.load();
    }

protected:
    BaseLogImpl(PageManager& pageManager);

    /**
     * @brief Acquires an empty log page from the page manager
     */
    LogPage* acquirePage() {
        return new(mPageManager.alloc()) LogPage();
    }

    /**
     * @brief Returns an unwritten log page (no entries appended) to the page manager
     */
    void freeEmptyPageNow(LogPage* page);

    /**
     * @brief Returns a log page to the page manager
     */
    void freePageNow(LogPage* page) {
        mPageManager.free(page);
    }

    /**
     * @brief Deletes all pages between the begin page and the end page (including begin but excluding end)
     *
     * Deletes pages using the Safe Memory Reclamation mechanism.
     *
     * @param begin The first page to be deleted
     * @param end The page succeeding the last deleted pages
     */
    void freePage(LogPage* begin, LogPage* end);

private:
    PageManager& mPageManager;

protected:
    std::atomic<LogPage*> mHead;
};

/**
 * @brief Log implementation providing no order guarantee when iterating over the elements
 *
 * Links pages together by storing a pointer to the preceeding page (i.e. the current head contains a pointer to the
 * previous head).
 *
 * Pages are iterated from the head (newest page) to the tail (oldest page).
 */
class UnorderedLogImpl : public BaseLogImpl {
protected:
    UnorderedLogImpl(PageManager& pageManager)
            : BaseLogImpl(pageManager) {
    }

    LogPage* createPage(LogPage* oldHead);

    /**
     * @brief Log iteration starts from the head
     */
    LogPage* startPage() {
        return mHead.load();
    }

    const LogPage* startPage() const {
        return mHead.load();
    }
};

/**
 * @brief Log implementation to iterate over the elements in insert order
 *
 * Links pages together by storing a pointer to the following page (i.e. the previous head contains a pointer to the
 * current head).
 *
 * Pages are iterated from the tail (oldest page) to the head (newest page). This allows the log elements to be iterated
 * from oldest written to newest written.
 */
class OrderedLogImpl : public BaseLogImpl {
public:
    LogPage* tail() {
        return mTail.load();
    }

    /**
     * @brief Tries to set the new tail page of the log from oldTail to newTail
     *
     * After this operation the log will begin at the new tail instead from the old tail. All truncated pages will be
     * freed by the Safe Memory Reclamation mechanism.
     *
     * @param oldTail The previous tail
     * @param newTail The new tail
     * @return True if the truncation succeeded
     */
    bool truncateLog(LogPage* oldTail, LogPage* newTail);

protected:
    OrderedLogImpl(PageManager& pageManager);

    LogPage* createPage(LogPage* oldHead);

    /**
     * @brief Log iteration starts from the tail
     */
    LogPage* startPage() {
        return mTail.load();
    }

    const LogPage* startPage() const {
        return mTail.load();
    }

private:
    std::atomic<LogPage*> mTail;
};

/**
 * @brief The Log class
 */
template <class Impl>
class Log : public Impl, NonCopyable {
public:
    /**
     * @brief Iterator for iterating over all pages in the log
     *
     * The order in which pages are iterated (i.e. head from tail, tail to head) is dependent on the chosen Log
     * implementation.
     */
    class PageIterator : public std::iterator<std::input_iterator_tag, LogPage> {
    public:
        PageIterator(LogPage* page)
                : mPage(page) {
        }

        PageIterator& operator++() {
            mPage = mPage->next().load();
            return *this;
        }

        PageIterator operator++(int) {
            PageIterator result(*this);
            operator++();
            return result;
        }

        bool operator==(const PageIterator& rhs) const {
            return (mPage == rhs.mPage);
        }

        bool operator!=(const PageIterator& rhs) const {
            return !operator==(rhs);
        }

        reference operator*() {
            return *operator->();
        }

        pointer operator->() const {
            return mPage;
        }

    private:
        /// Current page this iterator is pointing to
        LogPage* mPage;
    };

    /**
     * @brief Iterator for iterating over all entries in the log
     *
     * The order in which elements are iterated is dependent on the chosen Log implementation.
     */
    template<class EntryType>
    class LogIteratorImpl : public std::iterator<std::input_iterator_tag, typename std::conditional<std::is_const<EntryType>::value, const LogEntry, LogEntry>::type> {
    public:
        static constexpr bool is_const_iterator = std::is_const<typename std::remove_pointer<EntryType>::type>::value;
        using reference = typename std::conditional<is_const_iterator, const LogEntry&, LogEntry&>::type;
        using const_reference = const LogEntry&;
        using pointer = typename std::conditional<is_const_iterator, const LogEntry*, LogEntry*>::type;
        using const_pointer = const LogEntry*;
        LogIteratorImpl(EntryType page)
                : mPage(page),
                  mPageOffset(mPage ? mPage->offset() : 0),
                  mPos(0) {
        }

        LogPage* page() const {
            return mPage;
        }

        uint32_t offset() const {
            return mPos;
        }

        LogIteratorImpl& operator++() {
            auto entry = reinterpret_cast<pointer>(mPage->data() + mPos);
            mPos += entry->size();
            if (mPos == mPageOffset) {
                mPage = mPage->next().load();
                mPageOffset = (mPage ? mPage->offset() : 0);
                mPos = 0;
            }
            return *this;
        }

        LogIteratorImpl<EntryType> operator++(int) {
            LogIteratorImpl<EntryType> result(*this);
            operator++();
            return result;
        }

        bool operator==(const LogIteratorImpl<EntryType>& rhs) const {
            return ((mPage == rhs.mPage) && (mPos == rhs.mPos));
        }

        bool operator!=(const LogIteratorImpl<EntryType>& rhs) const {
            return !operator==(rhs);
        }

        reference operator*() {
            return *operator->();
        }

        const_reference operator*() const {
            return *operator->();
        }

        pointer operator->() {
            return reinterpret_cast<pointer>(mPage->data() + mPos);
        }

        const_pointer operator->() const {
            return reinterpret_cast<const_pointer>(mPage->data() + mPos);
        }

    private:
        /// Current page this iterator operates on
        EntryType mPage;

        /// Maximum offset of the current page
        uint32_t mPageOffset;

        /// Current offset the iterator is pointing to
        uint32_t mPos;
    };
    using LogIterator = LogIteratorImpl<LogPage*>;
    using ConstLogIterator = LogIteratorImpl<const LogPage*>;

    Log(PageManager& pageManager)
            : Impl(pageManager) {
    }

    ~Log();

    /**
     * @brief Appends a new entry to the log
     *
     * @param size Size of the new entry
     * @return Pointer to allocated LogEntry or nullptr if unable to allocate the entry
     */
    LogEntry* append(uint32_t size);

    /**
     * @brief Deletes all pages between the begin page and the end page (excluding both begin and end)
     *
     * After calling this function all active iterators will become invalid.
     *
     * This function is not 100 percent thread safe, multiple threads deleting overlapping regions will result in a
     * corrupted log.
     *
     * @param begin The page preceeding the first deleted page
     * @param end The page succeeding the last deleted page
     */
    void erase(LogPage* begin, LogPage* end);

    PageIterator pageBegin() {
        return PageIterator(Impl::startPage());
    }

    PageIterator pageEnd() {
        return PageIterator(nullptr);
    }

    LogIterator begin() {
        return LogIterator(Impl::startPage());
    }

    ConstLogIterator begin() const {
        return ConstLogIterator(Impl::startPage());
    }

    ConstLogIterator cbegin() const {
        return begin();
    }

    LogIterator end() {
        return LogIterator(nullptr);
    }

    ConstLogIterator end() const {
        return ConstLogIterator(nullptr);
    }

    ConstLogIterator cend() const {
        return end();
    }
};

extern template class Log<UnorderedLogImpl>;
extern template class Log<OrderedLogImpl>;

} // namespace store
} // namespace tell
<commit_msg>made const version of getEntryFromData<commit_after>#pragma once

#include <config.h>

#include "Logging.hpp"
#include "NonCopyable.hpp"
#include "PageManager.hpp"

#include <atomic>
#include <cstddef>
#include <iterator>

namespace tell {
namespace store {

/**
 * @brief A single entry in the log able to store any arbitrary data
 */
class LogEntry : NonCopyable, NonMovable {
public:
    /**
     * @brief Returns the LogEntry struct associated with a given data pointer
     *
     * The data pointer has to be the pointer that was obtained from LogEntry::data().
     *
     * @param data The data pointer
     * @return The LogEntry struct associated with the data pointer
     */
    static LogEntry* entryFromData(char* data) {
        return reinterpret_cast<LogEntry*>(data - sizeof(LogEntry));
    }

    static const LogEntry* entryFromData(const char* data) {
        return reinterpret_cast<const LogEntry*>(data - sizeof(LogEntry));
    }

    /**
     * @brief Constructor will never be called
     *
     * Everything will be zero initialized.
     */
    LogEntry() = delete;

    /**
     * @brief Destructor will never be called
     */
    ~LogEntry() = delete;

    /**
     * @brief The size of the entry in the log
     *
     * @note This is not the size of the pure data payload but the size of the whole (8 byte padded) log entry.
     */
    uint32_t size() const {
        return (mSize.load() & (0xFFFFFFFFu << 1));
    }

    char* data() {
        return reinterpret_cast<char*>(this) + sizeof(LogEntry);
    }

    const char* data() const {
        return reinterpret_cast<const char*>(this) + sizeof(LogEntry);
    }

    /**
     * @brief Whether the entry was written completely
     *
     * Checks if the LSB of size is 0.
     */
    bool sealed() const {
        return ((mSize.load() & 0x1u) == 0);
    }

    /**
     * @brief Seals the entry indicating that it has been completely written
     *
     * Sets the LSB of size to 0.
     */
    void seal() {
        LOG_ASSERT(!sealed(), "Entry is already sealed");
        mSize.fetch_and(0xFFFFFFFFu << 1);
    }

private:
    friend class LogPage;

    /**
     * @brief Tries to create a new LogEntry at this log position
     *
     * Acquires the space for this entry in the log by compare and swapping the size field from 0 to the given size.
     * Because the PageManager returns zeroed pages, the object is zero initalized (and size thus 0) if nobody has
     * written to it yet.
     *
     * @param size Size of the whole 8 byte padded log entry
     * @return 0 if the log entry was successfully acquired else the previously reserved size of this log entry
     */
    uint32_t tryAcquire(uint32_t size);

    /// Size of this LogEntry as the 8 byte aligned size of the LogEntry class plus data payload
    std::atomic<uint32_t> mSize;
};

/**
 * @brief A single page in the log containing multiple LogEntry's
 *
 * A Log-Page has the following form:
 *
 * -------------------------------------------------------------------------------
 * | next (8 bytes) | offset (4 bytes) | padding (4 bytes) | entry | entry | ... |
 * -------------------------------------------------------------------------------
 */
class LogPage : NonCopyable, NonMovable {
public:
    /// Size of the LogPage data structure
    static constexpr size_t LOG_HEADER_SIZE = 16;

    /// Maximum size of a log entry
    static constexpr uint32_t MAX_ENTRY_SIZE = TELL_PAGE_SIZE - LogPage::LOG_HEADER_SIZE;

    /// Maximum size of a log entries data payload
    static constexpr uint32_t MAX_DATA_SIZE = MAX_ENTRY_SIZE - sizeof(LogEntry);

    /**
     * @brief Iterator for iterating over all entries in a page
     */
    class EntryIterator : public std::iterator<std::input_iterator_tag, LogEntry> {
    public:
        EntryIterator(LogPage* page, uint32_t pos)
                : mPage(page),
                  mPos(pos) {
        }

        LogPage* page() const {
            return mPage;
        }

        uint32_t offset() const {
            return mPos;
        }

        EntryIterator& operator++() {
            auto entry = reinterpret_cast<LogEntry*>(mPage->data() + mPos);
            mPos += entry->size();
            return *this;
        }

        EntryIterator operator++(int) {
            EntryIterator result(*this);
            operator++();
            return result;
        }

        bool operator==(const EntryIterator& rhs) const {
            return ((mPage == rhs.mPage) && (mPos == rhs.mPos));
        }

        bool operator!=(const EntryIterator& rhs) const {
            return !operator==(rhs);
        }

        reference operator*() {
            return *operator->();
        }

        pointer operator->() const {
            return reinterpret_cast<LogEntry*>(mPage->data() + mPos);
        }

    private:
        /// Page this iterator belongs to
        LogPage* mPage;

        /// Current offset the iterator is pointing to
        uint32_t mPos;
    };

    LogPage()
            : mOffset(0x1u) {
    }

    char* data() {
        return reinterpret_cast<char*>(this) + LOG_HEADER_SIZE;
    }

    const char* data() const {
        return reinterpret_cast<const char*>(this) + LOG_HEADER_SIZE;
    }

    /**
     * @brief Appends a new entry to this log page
     *
     * @param size Size of the new entry
     * @return Pointer to allocated LogEntry or nullptr if unable to allocate the entry in this page
     */
    LogEntry* append(uint32_t size);

    /**
     * @brief Page preceeding the current page in the log
     */
    std::atomic<LogPage*>& next() {
        return mNext;
    }

    const std::atomic<LogPage*>& next() const {
        return mNext;
    }

    /**
     * @brief Current offset into the page
     */
    uint32_t offset() const {
        return (mOffset.load() & (0xFFFFFFFFu << 1));
    }

    EntryIterator begin() {
        return EntryIterator(this, 0x0u);
    }

    EntryIterator end() {
        return EntryIterator(this, offset());
    }

    /**
     * @brief Whether the page is sealed and as such write protected
     *
     * Checks if the LSB of offset is 0.
     */
    bool sealed() const {
        return ((mOffset.load() & 0x1u) == 0);
    }

    /**
     * @brief Seals the page indicating that it is write protected
     *
     * Sets the LSB of offset to 0.
     */
    void seal() {
        LOG_ASSERT(!sealed(), "Page is already sealed");
        mOffset.fetch_and(0xFFFFFFFFu << 1);
    }

    /**
     * @brief Tries to seal the page at the given offset
     *
     * Only seals the page if the current page offset is equal to the given offset.
     *
     * Compare and swaps the given offset with LSB 1 (not sealed) to the given offset with LSB 0 (sealed).
     *
     * @param offset The offset this page should be sealed at
     * @return Whether the page was successfully sealed
     */
    bool seal(uint32_t offset) {
        LOG_ASSERT(!sealed(), "Page is already sealed");
        LOG_ASSERT((offset & 0x1u) == 0, "LSB has to be zero");
        auto exp = (offset | 0x1u);
        return mOffset.compare_exchange_strong(exp, offset);
    }

private:
    template <class Impl> friend class Log;

    /**
     * @brief Appends a new entry to the log
     *
     * @param size Complete 8 byte padded size of the new entry
     * @return Pointer to allocated LogEntry or nullptr if unable to allocate the entry in this page
     */
    LogEntry* appendEntry(uint32_t size);

    std::atomic<LogPage*> mNext;
    std::atomic<uint32_t> mOffset;
};

/**
 * @brief Base class for Log implementations
 *
 * Provides the basic page handling mechanism and the head pointer.
 */
class BaseLogImpl {
public:
    LogPage* head() {
        return mHead.load();
    }

protected:
    BaseLogImpl(PageManager& pageManager);

    /**
     * @brief Acquires an empty log page from the page manager
     */
    LogPage* acquirePage() {
        return new(mPageManager.alloc()) LogPage();
    }

    /**
     * @brief Returns an unwritten log page (no entries appended) to the page manager
     */
    void freeEmptyPageNow(LogPage* page);

    /**
     * @brief Returns a log page to the page manager
     */
    void freePageNow(LogPage* page) {
        mPageManager.free(page);
    }

    /**
     * @brief Deletes all pages between the begin page and the end page (including begin but excluding end)
     *
     * Deletes pages using the Safe Memory Reclamation mechanism.
     *
     * @param begin The first page to be deleted
     * @param end The page succeeding the last deleted pages
     */
    void freePage(LogPage* begin, LogPage* end);

private:
    PageManager& mPageManager;

protected:
    std::atomic<LogPage*> mHead;
};

/**
 * @brief Log implementation providing no order guarantee when iterating over the elements
 *
 * Links pages together by storing a pointer to the preceeding page (i.e. the current head contains a pointer to the
 * previous head).
 *
 * Pages are iterated from the head (newest page) to the tail (oldest page).
 */
class UnorderedLogImpl : public BaseLogImpl {
protected:
    UnorderedLogImpl(PageManager& pageManager)
            : BaseLogImpl(pageManager) {
    }

    LogPage* createPage(LogPage* oldHead);

    /**
     * @brief Log iteration starts from the head
     */
    LogPage* startPage() {
        return mHead.load();
    }

    const LogPage* startPage() const {
        return mHead.load();
    }
};

/**
 * @brief Log implementation to iterate over the elements in insert order
 *
 * Links pages together by storing a pointer to the following page (i.e. the previous head contains a pointer to the
 * current head).
 *
 * Pages are iterated from the tail (oldest page) to the head (newest page). This allows the log elements to be iterated
 * from oldest written to newest written.
 */
class OrderedLogImpl : public BaseLogImpl {
public:
    LogPage* tail() {
        return mTail.load();
    }

    /**
     * @brief Tries to set the new tail page of the log from oldTail to newTail
     *
     * After this operation the log will begin at the new tail instead from the old tail. All truncated pages will be
     * freed by the Safe Memory Reclamation mechanism.
     *
     * @param oldTail The previous tail
     * @param newTail The new tail
     * @return True if the truncation succeeded
     */
    bool truncateLog(LogPage* oldTail, LogPage* newTail);

protected:
    OrderedLogImpl(PageManager& pageManager);

    LogPage* createPage(LogPage* oldHead);

    /**
     * @brief Log iteration starts from the tail
     */
    LogPage* startPage() {
        return mTail.load();
    }

    const LogPage* startPage() const {
        return mTail.load();
    }

private:
    std::atomic<LogPage*> mTail;
};

/**
 * @brief The Log class
 */
template <class Impl>
class Log : public Impl, NonCopyable {
public:
    /**
     * @brief Iterator for iterating over all pages in the log
     *
     * The order in which pages are iterated (i.e. head from tail, tail to head) is dependent on the chosen Log
     * implementation.
     */
    class PageIterator : public std::iterator<std::input_iterator_tag, LogPage> {
    public:
        PageIterator(LogPage* page)
                : mPage(page) {
        }

        PageIterator& operator++() {
            mPage = mPage->next().load();
            return *this;
        }

        PageIterator operator++(int) {
            PageIterator result(*this);
            operator++();
            return result;
        }

        bool operator==(const PageIterator& rhs) const {
            return (mPage == rhs.mPage);
        }

        bool operator!=(const PageIterator& rhs) const {
            return !operator==(rhs);
        }

        reference operator*() {
            return *operator->();
        }

        pointer operator->() const {
            return mPage;
        }

    private:
        /// Current page this iterator is pointing to
        LogPage* mPage;
    };

    /**
     * @brief Iterator for iterating over all entries in the log
     *
     * The order in which elements are iterated is dependent on the chosen Log implementation.
     */
    template<class EntryType>
    class LogIteratorImpl : public std::iterator<std::input_iterator_tag, typename std::conditional<std::is_const<EntryType>::value, const LogEntry, LogEntry>::type> {
    public:
        static constexpr bool is_const_iterator = std::is_const<typename std::remove_pointer<EntryType>::type>::value;
        using reference = typename std::conditional<is_const_iterator, const LogEntry&, LogEntry&>::type;
        using const_reference = const LogEntry&;
        using pointer = typename std::conditional<is_const_iterator, const LogEntry*, LogEntry*>::type;
        using const_pointer = const LogEntry*;
        LogIteratorImpl(EntryType page)
                : mPage(page),
                  mPageOffset(mPage ? mPage->offset() : 0),
                  mPos(0) {
        }

        LogPage* page() const {
            return mPage;
        }

        uint32_t offset() const {
            return mPos;
        }

        LogIteratorImpl& operator++() {
            auto entry = reinterpret_cast<pointer>(mPage->data() + mPos);
            mPos += entry->size();
            if (mPos == mPageOffset) {
                mPage = mPage->next().load();
                mPageOffset = (mPage ? mPage->offset() : 0);
                mPos = 0;
            }
            return *this;
        }

        LogIteratorImpl<EntryType> operator++(int) {
            LogIteratorImpl<EntryType> result(*this);
            operator++();
            return result;
        }

        bool operator==(const LogIteratorImpl<EntryType>& rhs) const {
            return ((mPage == rhs.mPage) && (mPos == rhs.mPos));
        }

        bool operator!=(const LogIteratorImpl<EntryType>& rhs) const {
            return !operator==(rhs);
        }

        reference operator*() {
            return *operator->();
        }

        const_reference operator*() const {
            return *operator->();
        }

        pointer operator->() {
            return reinterpret_cast<pointer>(mPage->data() + mPos);
        }

        const_pointer operator->() const {
            return reinterpret_cast<const_pointer>(mPage->data() + mPos);
        }

    private:
        /// Current page this iterator operates on
        EntryType mPage;

        /// Maximum offset of the current page
        uint32_t mPageOffset;

        /// Current offset the iterator is pointing to
        uint32_t mPos;
    };
    using LogIterator = LogIteratorImpl<LogPage*>;
    using ConstLogIterator = LogIteratorImpl<const LogPage*>;

    Log(PageManager& pageManager)
            : Impl(pageManager) {
    }

    ~Log();

    /**
     * @brief Appends a new entry to the log
     *
     * @param size Size of the new entry
     * @return Pointer to allocated LogEntry or nullptr if unable to allocate the entry
     */
    LogEntry* append(uint32_t size);

    /**
     * @brief Deletes all pages between the begin page and the end page (excluding both begin and end)
     *
     * After calling this function all active iterators will become invalid.
     *
     * This function is not 100 percent thread safe, multiple threads deleting overlapping regions will result in a
     * corrupted log.
     *
     * @param begin The page preceeding the first deleted page
     * @param end The page succeeding the last deleted page
     */
    void erase(LogPage* begin, LogPage* end);

    PageIterator pageBegin() {
        return PageIterator(Impl::startPage());
    }

    PageIterator pageEnd() {
        return PageIterator(nullptr);
    }

    LogIterator begin() {
        return LogIterator(Impl::startPage());
    }

    ConstLogIterator begin() const {
        return ConstLogIterator(Impl::startPage());
    }

    ConstLogIterator cbegin() const {
        return begin();
    }

    LogIterator end() {
        return LogIterator(nullptr);
    }

    ConstLogIterator end() const {
        return ConstLogIterator(nullptr);
    }

    ConstLogIterator cend() const {
        return end();
    }
};

extern template class Log<UnorderedLogImpl>;
extern template class Log<OrderedLogImpl>;

} // namespace store
} // namespace tell
<|endoftext|>


<commit_before>/*
    Copyright 2010 Google Inc.

    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.
 */

#include "GrAtlas.h"
#include "GrClipIterator.h"
#include "GrContext.h"
#include "GrTextContext.h"
#include "GrTextStrike.h"
#include "GrTextStrike_impl.h"
#include "GrFontScaler.h"

static const GrVertexLayout VLAYOUT = GrDrawTarget::kTextFormat_VertexLayoutBit;

void GrTextContext::flushGlyphs() {
    if (fCurrVertex > 0) {
        GrDrawTarget::AutoStateRestore asr(fDrawTarget);

        // setup our sampler state for our text texture/atlas

        GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
                               GrSamplerState::kRepeat_WrapMode,
                               GrSamplerState::kAlphaMod_SampleMode,
                               !fExtMatrix.isIdentity());
        fDrawTarget->setSamplerState(sampler);

        GrAssert(GrIsALIGN4(fCurrVertex));
        int nIndices = fCurrVertex + (fCurrVertex >> 1);
        GrAssert(fCurrTexture);
        fDrawTarget->setTexture(fCurrTexture);
        fDrawTarget->setTextureMatrix(GrMatrix::I());
        fDrawTarget->setIndexSourceToBuffer(fContext->quadIndexBuffer());

        fDrawTarget->drawIndexed(GrDrawTarget::kTriangles_PrimitiveType,
                                 0, 0, fCurrVertex, nIndices);

        fDrawTarget->releaseReservedGeometry();
        fVertices = NULL;
        fMaxVertices = 0;
        fCurrVertex = 0;
        fCurrTexture->unref();
        fCurrTexture = NULL;
    }
}

GrTextContext::GrTextContext(GrContext* context, const GrMatrix* extMatrix) {
    fContext = context;
    fStrike = NULL;

    fCurrTexture = NULL;
    fCurrVertex = 0;
    fClipRect = context->getClip().getBounds();

    if (NULL != extMatrix) {
        fExtMatrix = *extMatrix;
    } else {
        fExtMatrix = GrMatrix::I();
    }
    if (!fExtMatrix.isIdentity()) {
        GrMatrix inverse;
        GrRect r;
        r.set(fClipRect);
        if (fExtMatrix.invert(&inverse)) {
            inverse.mapRect(&r);
            r.roundOut(&fClipRect);
        }
    }

    fContext->getViewMatrix(&fOrigViewMatrix);
    fContext->setViewMatrix(fExtMatrix);

    fVertices = NULL;
    fMaxVertices = 0;
    fDrawTarget = fContext->getTextTarget();
}

GrTextContext::~GrTextContext() {
    this->flushGlyphs();
    fContext->setViewMatrix(fOrigViewMatrix);
}

void GrTextContext::flush() {
    this->flushGlyphs();
}

static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
                              int stride) {
    v[0 * stride].setI(l, t);
    v[1 * stride].setI(l, b);
    v[2 * stride].setI(r, b);
    v[3 * stride].setI(r, t);
}

void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
                                    GrFixed vx, GrFixed vy,
                                    GrFontScaler* scaler) {
    if (NULL == fStrike) {
        fStrike = fContext->getFontCache()->getStrike(scaler);
    }

    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
    if (NULL == glyph || glyph->fBounds.isEmpty()) {
        return;
    }

    vx += GrIntToFixed(glyph->fBounds.fLeft);
    vy += GrIntToFixed(glyph->fBounds.fTop);

    // keep them as ints until we've done the clip-test
    GrFixed width = glyph->fBounds.width();
    GrFixed height = glyph->fBounds.height();

    // check if we clipped out
    if (true || NULL == glyph->fAtlas) {
        int x = vx >> 16;
        int y = vy >> 16;
        if (fClipRect.quickReject(x, y, x + width, y + height)) {
//            Gr_clz(3);    // so we can set a break-point in the debugger
            return;
        }
    }

    if (NULL == glyph->fAtlas) {
        if (fStrike->getGlyphAtlas(glyph, scaler)) {
            goto HAS_ATLAS;
        }
        // try to purge
        fContext->getFontCache()->purgeExceptFor(fStrike);
        if (fStrike->getGlyphAtlas(glyph, scaler)) {
            goto HAS_ATLAS;
        }

        // Draw as a path, so we flush any accumulated glyphs first
        this->flushGlyphs();

        if (NULL == glyph->fPath) {

            GrPath* path = new GrPath;
            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
                // flag the glyph as being dead?
                delete path;
                return;
            }
            glyph->fPath = path;
        }
        GrPath::Iter iter(*glyph->fPath);
        bool useTexture = false;
        GrPoint translate;
        translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
                      GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
        fContext->drawPath(&iter, GrContext::kWinding_PathFill,
                              useTexture, &translate);
        return;
    }

HAS_ATLAS:
    GrAssert(glyph->fAtlas);

    // now promote them to fixed
    width = GrIntToFixed(width);
    height = GrIntToFixed(height);

    GrTexture* texture = glyph->fAtlas->texture();
    GrAssert(texture);

    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
        this->flushGlyphs();
        fCurrTexture = texture;
        fCurrTexture->ref();
    }

    if (NULL == fVertices) {
        // If we need to reserve vertices allow the draw target to suggest
        // a number of verts to reserve and whether to perform a flush.
        fMaxVertices = kMinRequestedVerts;
        bool flush = fDrawTarget->geometryHints(VLAYOUT,
                                               &fMaxVertices,
                                               NULL);
        if (flush) {
            this->flushGlyphs();
            fContext->flushText();
            fDrawTarget = fContext->getTextTarget();
            fMaxVertices = kDefaultRequestedVerts;
            // ignore return, no point in flushing again.
            fDrawTarget->geometryHints(VLAYOUT,
                                       &fMaxVertices,
                                       NULL);
        }

        if (fMaxVertices < kMinRequestedVerts) {
            fMaxVertices = kDefaultRequestedVerts;
        } else if (fMaxVertices > (fContext->maxQuadsInIndexBuffer() * 4)) {
            // don't exceed the limit of the index buffer
            fMaxVertices = (fContext->maxQuadsInIndexBuffer() * 4);
        }
        bool success = fDrawTarget->reserveAndLockGeometry(VLAYOUT,
                                                           fMaxVertices, 0,
                                                           (void**)&fVertices,
                                                           NULL);
        GrAlwaysAssert(success);
    }

    GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
    GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);

#if GR_GL_TEXT_TEXTURE_NORMALIZED
    int x = vx >> 16;
    int y = vy >> 16;
    int w = width >> 16;
    int h = height >> 16;

    setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
    setRectFan(&fVertices[2*fCurrVertex+1],
               texture->normalizeFixedX(tx),
               texture->normalizeFixedY(ty),
               texture->normalizeFixedX(tx + width),
               texture->normalizeFixedY(ty + height),
               2);
#else
    fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
                                        2 * sizeof(GrGpuTextVertex));
    fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
                                          texture->normalizeFixedY(ty),
                                          texture->normalizeFixedX(tx + width),
                                          texture->normalizeFixedY(ty + height),
                                          2 * sizeof(GrGpuTextVertex));
#endif
    fCurrVertex += 4;
}

<commit_msg>flush inorder drawtarget (i.e. flushText) before we purge the atlas/cache<commit_after>/*
    Copyright 2010 Google Inc.

    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.
 */

#include "GrAtlas.h"
#include "GrClipIterator.h"
#include "GrContext.h"
#include "GrTextContext.h"
#include "GrTextStrike.h"
#include "GrTextStrike_impl.h"
#include "GrFontScaler.h"

static const GrVertexLayout VLAYOUT = GrDrawTarget::kTextFormat_VertexLayoutBit;

void GrTextContext::flushGlyphs() {
    if (fCurrVertex > 0) {
        GrDrawTarget::AutoStateRestore asr(fDrawTarget);

        // setup our sampler state for our text texture/atlas

        GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
                               GrSamplerState::kRepeat_WrapMode,
                               GrSamplerState::kAlphaMod_SampleMode,
                               !fExtMatrix.isIdentity());
        fDrawTarget->setSamplerState(sampler);

        GrAssert(GrIsALIGN4(fCurrVertex));
        int nIndices = fCurrVertex + (fCurrVertex >> 1);
        GrAssert(fCurrTexture);
        fDrawTarget->setTexture(fCurrTexture);
        fDrawTarget->setTextureMatrix(GrMatrix::I());
        fDrawTarget->setIndexSourceToBuffer(fContext->quadIndexBuffer());

        fDrawTarget->drawIndexed(GrDrawTarget::kTriangles_PrimitiveType,
                                 0, 0, fCurrVertex, nIndices);

        fDrawTarget->releaseReservedGeometry();
        fVertices = NULL;
        fMaxVertices = 0;
        fCurrVertex = 0;
        fCurrTexture->unref();
        fCurrTexture = NULL;
    }
}

GrTextContext::GrTextContext(GrContext* context, const GrMatrix* extMatrix) {
    fContext = context;
    fStrike = NULL;

    fCurrTexture = NULL;
    fCurrVertex = 0;
    fClipRect = context->getClip().getBounds();

    if (NULL != extMatrix) {
        fExtMatrix = *extMatrix;
    } else {
        fExtMatrix = GrMatrix::I();
    }
    if (!fExtMatrix.isIdentity()) {
        GrMatrix inverse;
        GrRect r;
        r.set(fClipRect);
        if (fExtMatrix.invert(&inverse)) {
            inverse.mapRect(&r);
            r.roundOut(&fClipRect);
        }
    }

    fContext->getViewMatrix(&fOrigViewMatrix);
    fContext->setViewMatrix(fExtMatrix);

    fVertices = NULL;
    fMaxVertices = 0;
    fDrawTarget = fContext->getTextTarget();
}

GrTextContext::~GrTextContext() {
    this->flushGlyphs();
    fContext->setViewMatrix(fOrigViewMatrix);
}

void GrTextContext::flush() {
    this->flushGlyphs();
}

static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
                              int stride) {
    v[0 * stride].setI(l, t);
    v[1 * stride].setI(l, b);
    v[2 * stride].setI(r, b);
    v[3 * stride].setI(r, t);
}

void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
                                    GrFixed vx, GrFixed vy,
                                    GrFontScaler* scaler) {
    if (NULL == fStrike) {
        fStrike = fContext->getFontCache()->getStrike(scaler);
    }

    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
    if (NULL == glyph || glyph->fBounds.isEmpty()) {
        return;
    }

    vx += GrIntToFixed(glyph->fBounds.fLeft);
    vy += GrIntToFixed(glyph->fBounds.fTop);

    // keep them as ints until we've done the clip-test
    GrFixed width = glyph->fBounds.width();
    GrFixed height = glyph->fBounds.height();

    // check if we clipped out
    if (true || NULL == glyph->fAtlas) {
        int x = vx >> 16;
        int y = vy >> 16;
        if (fClipRect.quickReject(x, y, x + width, y + height)) {
//            Gr_clz(3);    // so we can set a break-point in the debugger
            return;
        }
    }

    if (NULL == glyph->fAtlas) {
        if (fStrike->getGlyphAtlas(glyph, scaler)) {
            goto HAS_ATLAS;
        }
        // must do this to flush inorder buffering before we purge
        fContext->flushText();

        // try to purge
        fContext->getFontCache()->purgeExceptFor(fStrike);
        if (fStrike->getGlyphAtlas(glyph, scaler)) {
            goto HAS_ATLAS;
        }

        // Draw as a path, so we flush any accumulated glyphs first
        this->flushGlyphs();

        if (NULL == glyph->fPath) {

            GrPath* path = new GrPath;
            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
                // flag the glyph as being dead?
                delete path;
                return;
            }
            glyph->fPath = path;
        }
        GrPath::Iter iter(*glyph->fPath);
        bool useTexture = false;
        GrPoint translate;
        translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
                      GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
        fContext->drawPath(&iter, GrContext::kWinding_PathFill,
                              useTexture, &translate);
        return;
    }

HAS_ATLAS:
    GrAssert(glyph->fAtlas);

    // now promote them to fixed
    width = GrIntToFixed(width);
    height = GrIntToFixed(height);

    GrTexture* texture = glyph->fAtlas->texture();
    GrAssert(texture);

    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
        this->flushGlyphs();
        fCurrTexture = texture;
        fCurrTexture->ref();
    }

    if (NULL == fVertices) {
        // If we need to reserve vertices allow the draw target to suggest
        // a number of verts to reserve and whether to perform a flush.
        fMaxVertices = kMinRequestedVerts;
        bool flush = fDrawTarget->geometryHints(VLAYOUT,
                                               &fMaxVertices,
                                               NULL);
        if (flush) {
            this->flushGlyphs();
            fContext->flushText();
            fDrawTarget = fContext->getTextTarget();
            fMaxVertices = kDefaultRequestedVerts;
            // ignore return, no point in flushing again.
            fDrawTarget->geometryHints(VLAYOUT,
                                       &fMaxVertices,
                                       NULL);
        }

        if (fMaxVertices < kMinRequestedVerts) {
            fMaxVertices = kDefaultRequestedVerts;
        } else if (fMaxVertices > (fContext->maxQuadsInIndexBuffer() * 4)) {
            // don't exceed the limit of the index buffer
            fMaxVertices = (fContext->maxQuadsInIndexBuffer() * 4);
        }
        bool success = fDrawTarget->reserveAndLockGeometry(VLAYOUT,
                                                           fMaxVertices, 0,
                                                           (void**)&fVertices,
                                                           NULL);
        GrAlwaysAssert(success);
    }

    GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
    GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);

#if GR_GL_TEXT_TEXTURE_NORMALIZED
    int x = vx >> 16;
    int y = vy >> 16;
    int w = width >> 16;
    int h = height >> 16;

    setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
    setRectFan(&fVertices[2*fCurrVertex+1],
               texture->normalizeFixedX(tx),
               texture->normalizeFixedY(ty),
               texture->normalizeFixedX(tx + width),
               texture->normalizeFixedY(ty + height),
               2);
#else
    fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
                                        2 * sizeof(GrGpuTextVertex));
    fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
                                          texture->normalizeFixedY(ty),
                                          texture->normalizeFixedX(tx + width),
                                          texture->normalizeFixedY(ty + height),
                                          2 * sizeof(GrGpuTextVertex));
#endif
    fCurrVertex += 4;
}

<|endoftext|>


<commit_before>/* Copyright 2012 K2Informatics GmbH, Root Laengenbold, Switzerland
 *
 * 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.
 */
#include "stdafx.h"

#include "erl_interface.h"
#include "ei.h"

#include "oci_marshal.h"
#include "cmd_processors.h"
#include "oci_lib_intf.h"

#define THREAD          10
#define QUEUE           256

#ifdef __WIN32__
#include <windows.h>
#include <tchar.h>
#else
#include <sys/time.h>
#include <event.h>
#include <stdlib.h>
#include <unistd.h>
#include "threadpool.h"
#endif

#include <stdio.h>

#ifdef __WIN32__
	static PTP_POOL pool = NULL;
	static TP_CALLBACK_ENVIRON CallBackEnviron;
	static PTP_CLEANUP_GROUP cleanupgroup = NULL;
	static UINT rollback = 0;
#else
	static threadpool_t *pTp            = NULL;
#endif

void InitializeThreadPool(void)
{
    REMOTE_LOG(DBG, "Port: Initializing Thread pool...\n");

#ifdef __WIN32__
    InitializeThreadpoolEnvironment(&CallBackEnviron);

    //
    // Create a custom, dedicated thread pool.
    //
    pool = CreateThreadpool(NULL);

    if (NULL == pool) {
        _tprintf(_T("CreateThreadpool failed. LastError: %u\n"), GetLastError());
        goto main_cleanup;
    }

    rollback = 1; // pool creation succeeded

    //
    // The thread pool is made persistent simply by setting
    // both the minimum and maximum threads to 1.
    //
    SetThreadpoolThreadMaximum(pool, THREAD);

    if (FALSE == SetThreadpoolThreadMinimum(pool, 1)) {
        _tprintf(_T("SetThreadpoolThreadMinimum failed. LastError: %u\n"),
                 GetLastError());
        goto main_cleanup;
    }

    //
    // Create a cleanup group for this thread pool.
    //
    cleanupgroup = CreateThreadpoolCleanupGroup();

    if (NULL == cleanupgroup) {
        _tprintf(_T("CreateThreadpoolCleanupGroup failed. LastError: %u\n"), GetLastError());
        goto main_cleanup;
    }

    rollback = 2;  // Cleanup group creation succeeded

    //
    // Associate the callback environment with our thread pool.
    //
    SetThreadpoolCallbackPool(&CallBackEnviron, pool);

    //
    // Associate the cleanup group with our thread pool.
    // Objects created with the same callback environment
    // as the cleanup group become members of the cleanup group.
    //
    SetThreadpoolCallbackCleanupGroup(&CallBackEnviron, cleanupgroup, NULL);
	return;

main_cleanup:
    CloseThreadpool(pool);
    pool = NULL;
#else
    pTp = threadpool_create(THREAD, QUEUE, 0);
    if(NULL != pTp)
        return;
#endif
    exit(0);
}

void CleanupThreadPool(void)
{
    REMOTE_LOG(DBG, "Port: Cleanup Thread pool...");

#ifdef __WIN32__
    BOOL bRet = FALSE;

    //
    // Wait for all callbacks to finish.
    // CloseThreadpoolCleanupGroupMembers also releases objects
    // that are members of the cleanup group, so it is not necessary
    // to call close functions on individual objects
    // after calling CloseThreadpoolCleanupGroupMembers.
    //
    CloseThreadpoolCleanupGroupMembers(cleanupgroup, FALSE, NULL);

    //
    // Already cleaned up the work item with the
    // CloseThreadpoolCleanupGroupMembers, so set rollback to 2.
    //
    rollback = 2;
    goto main_cleanup;

main_cleanup:
    //
    // Clean up any individual pieces manually
    // Notice the fall-through structure of the switch.
    // Clean up in reverse order.
    //

    switch (rollback) {
    case 3:
        // Clean up the cleanup group members.
        CloseThreadpoolCleanupGroupMembers(cleanupgroup,FALSE,NULL);
    case 2:
        // Clean up the cleanup group.
        CloseThreadpoolCleanupGroup(cleanupgroup);
    case 1:
        // Clean up the pool.
        CloseThreadpool(pool);

    default:
        break;
    }
#else
    if(pTp)
        threadpool_destroy(pTp, 0);
#endif
}

//
// This is the thread pool work callback function.
//
static
#ifdef __WIN32__
VOID CALLBACK
#else
void
#endif
ProcessCommandCb(
#ifdef __WIN32__
    PTP_CALLBACK_INSTANCE Instance,
    PVOID                 arg,
    PTP_WORK              Work
#else
    void *arg
#endif
)
{
    //REMOTE_LOG(DBG, "Port: WorkThread processing command...\n");
#ifdef __WIN32__
    // Instance, Parameter, and Work not used in this example.
    UNREFERENCED_PARAMETER(Instance);
    UNREFERENCED_PARAMETER(arg);
    UNREFERENCED_PARAMETER(Work);
#endif

	pkt rxpkt;
	while (!pop_cmd_queue(rxpkt));
	ProcessCommand();

	void * cmd_tuple = erl_decode((unsigned char *)rxpkt.buf);
	if (!cmd_tuple) {
        REMOTE_LOG(CRT, "Term (%d) decoding failed...", rxpkt.len);
		DUMP("rxpkt.buf", rxpkt.len, rxpkt.buf);
		if(NULL != rxpkt.buf) {
			delete rxpkt.buf;
			rxpkt.buf = NULL;
		}
		exit(1);
    }

    if(cmd_processor(cmd_tuple))
		exit(1);

	erl_free_compound((ETERM*)cmd_tuple);
	if(NULL != rxpkt.buf) delete rxpkt.buf;

	// ETERM memory house keeping
	unsigned long allocated, freed;
	erl_eterm_statistics(&allocated,&freed);
	if(freed * 2 < allocated) {
		REMOTE_LOG(WRN, "ETERM alloc limit alloc %lu freed %lu!\n", allocated, freed);
		erl_eterm_release();
	}

	return;
}

void ProcessCommand()
{
    //REMOTE_LOG(DBG, "Port: Delegating command processing to WorkThread...");
#ifdef __WIN32__
    PTP_WORK work = NULL;
    //
    // Create work with the callback environment.
    //
    work = CreateThreadpoolWork(ProcessCommandCb, NULL, &CallBackEnviron);

    if (NULL == work) {
        REMOTE_LOG(CRT, "CreateThreadpoolWork failed. LastError: %u\n", GetLastError());
        exit(0);
    }

    rollback = 3;  // Creation of work succeeded

    //
    // Submit the work to the pool. Because this was a pre-allocated
    // work item (using CreateThreadpoolWork), it is guaranteed to execute.
    //
    SubmitThreadpoolWork(work);
#else
    threadpool_add(pTp, &ProcessCommandCb, NULL, 0);
#endif
}
<commit_msg>yield on thread idle<commit_after>/* Copyright 2012 K2Informatics GmbH, Root Laengenbold, Switzerland
 *
 * 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.
 */
#include "stdafx.h"

#include "erl_interface.h"
#include "ei.h"

#include "oci_marshal.h"
#include "cmd_processors.h"
#include "oci_lib_intf.h"

#define THREAD          10
#define QUEUE           256

#ifdef __WIN32__
#include <windows.h>
#include <tchar.h>
#else
#include <sys/time.h>
#include <event.h>
#include <stdlib.h>
#include <unistd.h>
#include "threadpool.h"
#endif

#include <stdio.h>

#ifdef __WIN32__
	static PTP_POOL pool = NULL;
	static TP_CALLBACK_ENVIRON CallBackEnviron;
	static PTP_CLEANUP_GROUP cleanupgroup = NULL;
	static UINT rollback = 0;
#else
	static threadpool_t *pTp            = NULL;
#endif

void InitializeThreadPool(void)
{
    REMOTE_LOG(DBG, "Port: Initializing Thread pool...\n");

#ifdef __WIN32__
    InitializeThreadpoolEnvironment(&CallBackEnviron);

    //
    // Create a custom, dedicated thread pool.
    //
    pool = CreateThreadpool(NULL);

    if (NULL == pool) {
        _tprintf(_T("CreateThreadpool failed. LastError: %u\n"), GetLastError());
        goto main_cleanup;
    }

    rollback = 1; // pool creation succeeded

    //
    // The thread pool is made persistent simply by setting
    // both the minimum and maximum threads to 1.
    //
    SetThreadpoolThreadMaximum(pool, THREAD);

    if (FALSE == SetThreadpoolThreadMinimum(pool, 1)) {
        _tprintf(_T("SetThreadpoolThreadMinimum failed. LastError: %u\n"),
                 GetLastError());
        goto main_cleanup;
    }

    //
    // Create a cleanup group for this thread pool.
    //
    cleanupgroup = CreateThreadpoolCleanupGroup();

    if (NULL == cleanupgroup) {
        _tprintf(_T("CreateThreadpoolCleanupGroup failed. LastError: %u\n"), GetLastError());
        goto main_cleanup;
    }

    rollback = 2;  // Cleanup group creation succeeded

    //
    // Associate the callback environment with our thread pool.
    //
    SetThreadpoolCallbackPool(&CallBackEnviron, pool);

    //
    // Associate the cleanup group with our thread pool.
    // Objects created with the same callback environment
    // as the cleanup group become members of the cleanup group.
    //
    SetThreadpoolCallbackCleanupGroup(&CallBackEnviron, cleanupgroup, NULL);
	return;

main_cleanup:
    CloseThreadpool(pool);
    pool = NULL;
#else
    pTp = threadpool_create(THREAD, QUEUE, 0);
    if(NULL != pTp)
        return;
#endif
    exit(0);
}

void CleanupThreadPool(void)
{
    REMOTE_LOG(DBG, "Port: Cleanup Thread pool...");

#ifdef __WIN32__
    BOOL bRet = FALSE;

    //
    // Wait for all callbacks to finish.
    // CloseThreadpoolCleanupGroupMembers also releases objects
    // that are members of the cleanup group, so it is not necessary
    // to call close functions on individual objects
    // after calling CloseThreadpoolCleanupGroupMembers.
    //
    CloseThreadpoolCleanupGroupMembers(cleanupgroup, FALSE, NULL);

    //
    // Already cleaned up the work item with the
    // CloseThreadpoolCleanupGroupMembers, so set rollback to 2.
    //
    rollback = 2;
    goto main_cleanup;

main_cleanup:
    //
    // Clean up any individual pieces manually
    // Notice the fall-through structure of the switch.
    // Clean up in reverse order.
    //

    switch (rollback) {
    case 3:
        // Clean up the cleanup group members.
        CloseThreadpoolCleanupGroupMembers(cleanupgroup,FALSE,NULL);
    case 2:
        // Clean up the cleanup group.
        CloseThreadpoolCleanupGroup(cleanupgroup);
    case 1:
        // Clean up the pool.
        CloseThreadpool(pool);

    default:
        break;
    }
#else
    if(pTp)
        threadpool_destroy(pTp, 0);
#endif
}

//
// This is the thread pool work callback function.
//
static
#ifdef __WIN32__
VOID CALLBACK
#else
void
#endif
ProcessCommandCb(
#ifdef __WIN32__
    PTP_CALLBACK_INSTANCE Instance,
    PVOID                 arg,
    PTP_WORK              Work
#else
    void *arg
#endif
)
{
    //REMOTE_LOG(DBG, "Port: WorkThread processing command...\n");
#ifdef __WIN32__
    // Instance, Parameter, and Work not used in this example.
    UNREFERENCED_PARAMETER(Instance);
    UNREFERENCED_PARAMETER(arg);
    UNREFERENCED_PARAMETER(Work);
#endif

	pkt rxpkt;
	while (!pop_cmd_queue(rxpkt))
#ifdef __WIN32__
		SwitchToThread();
#else
		pthread_yield();
#endif
	ProcessCommand();

	void * cmd_tuple = erl_decode((unsigned char *)rxpkt.buf);
	if (!cmd_tuple) {
        REMOTE_LOG(CRT, "Term (%d) decoding failed...", rxpkt.len);
		DUMP("rxpkt.buf", rxpkt.len, rxpkt.buf);
		if(NULL != rxpkt.buf) {
			delete rxpkt.buf;
			rxpkt.buf = NULL;
		}
		exit(1);
    }

    if(cmd_processor(cmd_tuple))
		exit(1);

	erl_free_compound((ETERM*)cmd_tuple);
	if(NULL != rxpkt.buf) delete rxpkt.buf;

	// ETERM memory house keeping
	unsigned long allocated, freed;
	erl_eterm_statistics(&allocated,&freed);
	if(freed * 2 < allocated) {
		REMOTE_LOG(WRN, "ETERM alloc limit alloc %lu freed %lu!\n", allocated, freed);
		erl_eterm_release();
	}

	return;
}

void ProcessCommand()
{
    //REMOTE_LOG(DBG, "Port: Delegating command processing to WorkThread...");
#ifdef __WIN32__
    PTP_WORK work = NULL;
    //
    // Create work with the callback environment.
    //
    work = CreateThreadpoolWork(ProcessCommandCb, NULL, &CallBackEnviron);

    if (NULL == work) {
        REMOTE_LOG(CRT, "CreateThreadpoolWork failed. LastError: %u\n", GetLastError());
        exit(0);
    }

    rollback = 3;  // Creation of work succeeded

    //
    // Submit the work to the pool. Because this was a pre-allocated
    // work item (using CreateThreadpoolWork), it is guaranteed to execute.
    //
    SubmitThreadpoolWork(work);
#else
    threadpool_add(pTp, &ProcessCommandCb, NULL, 0);
#endif
}
<|endoftext|>


<commit_before><commit_msg>Fix BHO registration snafu caused by r58197.<commit_after><|endoftext|>


<commit_before><commit_msg>WaE: implicit conversion of NULL constant to 'boost::detail::sp_nullptr_t'<commit_after><|endoftext|>


<commit_before><commit_msg>Hopefully the last of the changes to fix the ChromeFrame InstallFlowTest failures. Basically a dumb error caused by having a local variable with the same name as that of the parameter to a function.<commit_after><|endoftext|>