• 로그인 함 해보끄나?

  • Sarangnamu.net June 17, 2003
    Home Login Profile Study Open Project Q&A Google Code
    CJpeglibExtension
    Last Modify : 10 December, 2010(12:13)
    /**
     * @file JpeglibExtension.h
     * @author cheol-dong choi <aucd29@gmail.com>
     * @version 1.0
     * @date April 9, 2009 13:26:31
     * @section LICENSE
     *
     * Copyright (c) 2003-2010, cheol-dong choi, (http://www.sarangnamu.net)
     *
     * 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.
     * 
     * @section DESCRIPTION
     *
     *
     * @section CHANGE_LOG
     *
     */
    
    
    
    #ifndef __CD_JPEGLIBEXTENSION_H__
    #define __CD_JPEGLIBEXTENSION_H__
    
    #include <stdlib.h>
    #include <string.h>
    #include <setjmp.h>
    #include <stdio.h>
    #include <string>
    
    /* The maximum number of palette entries in palette-based images.
    In the wonderful new world of gd 2.0, you can of course have
    many more colors when using truecolor mode. */
    
    #define gdMaxColors 256
    
    extern "C"
    {
    #include "jpeglib.h"
    }
    
    #define MAXSIZE     2048                    /* Maximum size of thumbnail   */
    #define MINSIZE     1                       /* Minimum size of thumbnail   */
    #define DEFAULTSIZE 128                     /* Default size of thumbnail   */
    
    #define GRAY 128
    
    #define RSZ_AUTO   0
    #define RSZ_WIDTH  1
    #define RSZ_HEIGHT 2
    #define RSZ_FIX    3
    
    #ifndef byte
    typedef unsigned char byte;
    #endif
    
    struct my_error_mgr
    {
    	struct jpeg_error_mgr pub;  /* "public" fields      */
    	jmp_buf setjmp_buffer;      /* for return to caller */
    };
    
    typedef struct my_error_mgr * my_error_ptr;
    
    static void my_error_exit(j_common_ptr cinfo)
    {
    	my_error_ptr myerr = (my_error_ptr) cinfo->err;
    	char buf[JMSG_LENGTH_MAX];
    	(*cinfo->err->format_message)(cinfo,buf);
    	longjmp(myerr->setjmp_buffer, 1);
    }
    
    class CJpeglibExtension
    {
    public:
    	CJpeglibExtension(void);
    
    	~CJpeglibExtension(void);
    
    public:
    	bool Read(const char* filename);
    
    	bool Read(const char* szPath, const char* szName);
    
    	bool Resize(int maxsize=DEFAULTSIZE, int which_dim=RSZ_AUTO);
    
    	bool Matrix(int nCount=3);
    
    	bool Write(const char *filename, int quality, bool bMatrix=false);
    
    	void FreeMemory(bool bMatrix=false);
    
    	byte* GetPalette() { return _palette; }
    
    	byte* GetReadImage() { return _readImage; }
    
    	int GetWidth() { return _width; }
    
    	int GetHeight() { return _height; }
    
    	bool IsDivide(int nDivCount, int& nX, int& nY);
    
    protected:
    	byte* _readImage;
    
    	byte* _outimage;                 /* The current thumbnail image */
    
    	byte* _palette;                  /* Global palette pointer      */
    
    	int _width;                      /* Original image width        */
    
    	int _height;                     /* Original image height       */
    
    	int _background;                 /* Index's background color    */
    
    	int _dwEffWidth;
    
    	int _wBpp;
    
    	int _biClrUsed;
    
    	long _out_wide;                  /* Width of thumbnail image    */
    
    	long _out_high;                  /* Height of thumbnail image   */
    
    	std::string _szFileName;
    };
    
    #endif
    
    /**
     * @file JpeglibExtension.cpp
     * @author cheol-dong choi <aucd29@gmail.com>
     * @version 1.0
     * @date April 9, 2009 13:26:27
     * @section LICENSE
     *
     * Copyright (c) 2003-2010, cheol-dong choi, (http://www.sarangnamu.net)
     *
     * 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.
     * 
     * @section DESCRIPTION
     *
     *
     * @section CHANGE_LOG
     *
     */
    
    
    
    #include "JpeglibExtension.h"
    
    #include "RSLOGGER.H"
    
    #if defined(_WIN32_WCE)
    #define min(a, b) (((a) < (b)) ? (a) : (b))
    #endif
    
    
    CJpeglibExtension::CJpeglibExtension(void)
    {
    	_outimage   = NULL;
    	_readImage  = NULL;
    	_palette    = NULL;
    
    	_width      = 0;                  /* Original image width        */
    	_height     = 0;                  /* Original image height       */
    	_background = 0;                  /* Index's background color    */
    	_out_wide   = 0;                  /* Width of thumbnail image    */
    	_out_high   = 0;                  /* Height of thumbnail image   */
    
    	_szFileName = "";
    }
    
    CJpeglibExtension::~CJpeglibExtension(void)
    {
    	FreeMemory();
    }
    
    // -----------------------------------------------------------
    
    bool CJpeglibExtension::Read(const char* filename)
    {
    	struct jpeg_decompress_struct cinfo;
    	struct my_error_mgr jerr;
    
    	int f;
    	int row_stride;
    	FILE* infile;
    	byte* ptr;
    
    	FreeMemory();
    
    	_szFileName = filename;
    
    #if !defined(ANDROID)
    	try
    	{
    #endif
    		_palette = (byte*)calloc(1, 1768);
    
    		if (_palette == NULL)
    		{
    #if !defined(ANDROID)
    			throw "Out of memory!!";
    #else
    			ASSERT(0);
    #endif
    		}
    
    		infile = fopen(filename,"rb");
    		if (infile == NULL)
    		{
    #if !defined(ANDROID)
    			throw "File not found!!";
    #else
    			ASSERT(0);
    #endif
    		}
    
    		cinfo.err           = jpeg_std_error(&jerr.pub);
    		jerr.pub.error_exit = my_error_exit;
    
    		if (setjmp(jerr.setjmp_buffer))
    		{
    			jpeg_destroy_decompress(&cinfo);
    
    			fclose(infile);
    			delete[] _palette;
    
    #if !defined(ANDROID)
    			throw "setjmp";
    #else
    			ASSERT(0);
    #endif
    		}
    
    		/* Now we can initialize the JPEG decompression object. */
    		jpeg_create_decompress(&cinfo);
    		jpeg_stdio_src(&cinfo,infile);
    		jpeg_read_header(&cinfo,TRUE);
    
    		/* setup parameters for decompression */
    		cinfo.dct_method                = JDCT_ISLOW;
    		cinfo.quantize_colors           = TRUE;
    		cinfo.desired_number_of_colors  = 256;
    		cinfo.two_pass_quantize         = TRUE;
    
    		/* Fix to greys if greyscale. (required to read greyscale JPEGs) */
    		if (cinfo.jpeg_color_space == JCS_GRAYSCALE)
    		{
    			cinfo.out_color_space           = JCS_GRAYSCALE;
    			cinfo.desired_number_of_colors  = 256;
    			cinfo.quantize_colors           = FALSE;
    			cinfo.two_pass_quantize         = FALSE;
    
    			for(f=0; f<256; f++)
    			{
    				_palette[f] = _palette[256 + f] = _palette[512 + f] = f;
    			}
    		}
    
    		_width      = cinfo.image_width;
    		_height     = cinfo.image_height;
    		_readImage  = (byte*)calloc(1, _width * _height);
    
    		if (_readImage == NULL)
    		{
    			longjmp(jerr.setjmp_buffer,1);
    #if !defined(ANDROID)
    			throw "Out of memory!!";
    #else
    			ASSERT(0);
    #endif
    		}
    
    		jpeg_start_decompress(&cinfo);
    
    		int wBpp = (cinfo.output_components * 8);
    
    		// Make sure bits per pixel is valid
    		if (wBpp <= 1)
    		{
    			_wBpp      = 1;
    			_biClrUsed = 2;
    		}
    		else if (wBpp <= 4)
    		{
    			_wBpp      = 4;
    			_biClrUsed = 16;
    		}
    		else if (wBpp <= 8)
    		{
    			_wBpp      = 8;
    			_biClrUsed = 256;
    		}
    		else
    		{
    			_wBpp      = 24;
    			_biClrUsed = 0;
    		}
    
    		// set the common image informations
    		_dwEffWidth = ((((_wBpp * _width) + 31) / 32) * 4);
    
    		/* read the palette (if greyscale, this has already been done) */
    		if (cinfo.jpeg_color_space != JCS_GRAYSCALE)
    		{
    			for (f=0; f<cinfo.actual_number_of_colors; f++)
    			{
    				_palette[    f] = cinfo.colormap[0][f];
    				_palette[256+f] = cinfo.colormap[1][f];
    				_palette[512+f] = cinfo.colormap[2][f];
    			}
    		}
    
    		if (cinfo.out_color_space == JCS_GRAYSCALE)
    		{
    			_biClrUsed = 256;
    		}
    		else
    		{
    			if (cinfo.quantize_colors)
    			{
    				_biClrUsed = cinfo.actual_number_of_colors;
    			}
    			else
    			{
    				_biClrUsed = 0;
    			}
    		}
    
    		ptr         = _readImage;
    		row_stride  = _width;
    
    		/* read the image */
    		while (cinfo.output_scanline < (unsigned int)_height)
    		{
    			jpeg_read_scanlines(&cinfo, &ptr, 1);
    			ptr += row_stride;
    		}
    
    		jpeg_finish_decompress(&cinfo);
    		jpeg_destroy_decompress(&cinfo);
    
    		fclose(infile);
    
    #if !defined(ANDROID)
    	}
    	catch (const char* e)
    	{
    		printf("Error!! %s\n", e);
    
    		return false;
    	}
    #endif
    
    	return true;
    }
    
    
    //
    // Read
    //
    
    bool CJpeglibExtension::Read(const char* szPath, const char* szName)
    {
    	LOG1("JpeglibExtension::Read\n");
    
    	//
    	// Method description
    	// -------
    	// 
    	//
    
    	if (szPath == NULL || szName == NULL)
    	{
    		LOG3("ERROR::CJpeglibExtension::Read <szPath == NULL || szName == NULL>\n");
    
    		return false;
    	}
    
    	std::string szFullPath;
    	size_t i;
    
    	szFullPath = szPath;
    	i = strlen(szPath);
    
    	if (szPath[i - 1] != '/' && szPath[i - 1] != '\\')
    	{
    		szFullPath += "/";
    	}
    	
    	szFullPath += szName;
    
    	return Read(szFullPath.c_str());
    }
    
    
    bool CJpeglibExtension::Write(const char *filename, int quality, bool bMatrix/*=true*/)
    {
    	struct jpeg_compress_struct cinfo;
    	struct jpeg_error_mgr       jerr;
    	FILE     *out;
    	JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s]          */
    	int      row_stride;        /* physical row width in image buffer */
    
    #if !defined(ANDROID)
    	try
    	{
    #endif
    		/* Step 1: Allocate and initialize JPEG compression object. */
    		cinfo.err = jpeg_std_error(&jerr);
    		jpeg_create_compress(&cinfo);
    
    		/* Step 2: Specify data destination. */
    		if (filename == NULL)
    		{
    #if !defined(ANDROID)
    			throw "Invalid filename";
    #else
    			ASSERT(0);
    #endif
    		}
    
    		if (filename)
    		{
    			out = fopen(filename, "wb");
    
    			if (out == NULL)
    			{
    #if !defined(ANDROID)
    				throw "can not open files";
    #else
    				ASSERT(0);
    #endif
    			}
    		}
    
    		jpeg_stdio_dest(&cinfo,out);
    
    		/* Step 3: set parameters for compression */
    		cinfo.image_width      = _out_wide;  /* image width and height, in pix */
    		cinfo.image_height     = _out_high;
    		cinfo.input_components = 3;         /* # of color components per pix  */
    		cinfo.in_color_space   = JCS_RGB;   /* colorspace of input image      */
    
    		jpeg_set_defaults(&cinfo);
    		jpeg_set_quality(&cinfo,quality,FALSE);
    
    		/* Step 4: Start compressor */
    		jpeg_start_compress(&cinfo,TRUE);
    
    		/* Step 5: while (scan lines remain to be written) */
    		row_stride = _out_wide * 3;    /* JSAMPLEs per row in image_buffer */
    
    		while (cinfo.next_scanline < cinfo.image_height)
    		{
    			row_pointer[0] = &_outimage[cinfo.next_scanline*row_stride];
    			jpeg_write_scanlines(&cinfo,row_pointer,1);
    		}
    
    		/* Step 6: Finish compression */
    		jpeg_finish_compress(&cinfo);
    
    		fclose(out);
    
    		/* Step 7: release JPEG compression object */
    		jpeg_destroy_compress(&cinfo);
    
    #if !defined(ANDROID)
    	}
    	catch (const char* e)
    	{
    		printf("Error!! %s\n", e);
    		return false;
    	}
    #endif
    
    	FreeMemory(bMatrix);
    
    	return true;
    }
    
    bool CJpeglibExtension::Resize(int maxsize, int resize_dim)
    {
    	register int   a, b, x, yp, yw;
    	long  y, sw, sh, lastyp;
    	int   c, pixwide, pixhigh;
    	int   tmp, tr, tg, tb, tn;
    	int   xypos;
    
    #if !defined(ANDROID)
    	try
    	{
    #endif
    		if (_readImage == NULL)
    		{
    #if !defined(ANDROID)
    			throw "Please read file!!";
    #else
    			ASSERT(0);
    #endif
    
    			return false;
    		}
    
    		byte* palr = GetPalette();
    		byte* palg = GetPalette() + 256;
    		byte* palb = GetPalette() + 512;
    
    		/* The resize dimension wasn't provided... */
    		if (!resize_dim)
    		{
    			if (_width <= _height)
    			{
    				/* Picture is square or portraity */
    				resize_dim = RSZ_HEIGHT;
    			}
    			else
    			{
    				/* Picture is landscapey */
    				resize_dim = RSZ_WIDTH;
    			}
    		}
    
    		switch(resize_dim)
    		{
    		case RSZ_FIX:
    			sh = sw = maxsize;
    			break;
    
    		case RSZ_HEIGHT:
    			sh = maxsize;
    			sw = (int)( (maxsize * ((double)_width)) / ((double)_height) );
    			break;
    
    		case RSZ_WIDTH:
    			sw = maxsize;
    			sh = (int)( (maxsize * ((double)_height)) / ((double)_width) );
    			break;
    
    		default:
    			/* WE SHOULD NEVER GET HERE! */
    			sw = sh = 0;
    			break;
    		}
    
    		/* This is a kludge. */
    		if (sw == 0) sw += 1;
    		if (sh == 0) sh += 1;
    
    		_out_wide = sw;
    		_out_high = sh;
    
    		/* Reserve the amount of memory we will need. */
    		_outimage = (byte*)calloc(1, (sw * sh * 3));
    		if (_outimage == NULL)
    		{
    #if !defined(ANDROID)
    			throw "Out of memory!!";
    #else
    			ASSERT(0);
    #endif
    		}
    
    		for (x=0; x<(sw * sh * 3); x++)
    		{
    			_outimage[x] = GRAY;
    		}
    
    		/* It has been reduced. */
    		if (_width > sw)
    		{
    			lastyp  = -1;
    			pixhigh = (int)( ((float)_height) / ((float)sh) + 0.5 );
    			pixwide = (int)( ((float)_width)  / ((float)sw) + 0.5 );
    			pixhigh++;
    			pixwide++;
    
    			for(y=0; y < _height; y++)
    			{
    				yp = (y * sh) / _height;
    
    				if (yp != lastyp)
    				{
    					yw = (y * _width);
    
    					/* we try to resample a bit. get that pentium RSN :) */
    					for (x=0; x < _width; x++,yw++)
    					{
    						tr = 0;
    						tg = 0;
    						tb = 0;
    						tn = 0;
    
    						for (b=0; (b < pixhigh) && (y+b < _height); b++)
    						{
    							for (a=0; (a < pixwide) && (x + a < _width); a++)
    							{
    								tmp = *(_readImage + yw + a + b * _width);
    								tr  += palr[tmp];
    								tg  += palg[tmp];
    								tb  += palb[tmp];
    								tn++;
    							}
    						}
    
    						xypos = 3 * (((x * sw) / _width) + yp * sw);
    
    						tr /= tn;
    						tg /= tn;
    						tb /= tn;
    
    						_outimage[xypos]   = tr;
    						_outimage[xypos+1] = tg;
    						_outimage[xypos+2] = tb;
    					}
    
    					lastyp = yp;
    				}
    			}
    		}
    		else
    		{
    			/* we just leave it the same size */
    
    			_out_wide = _width;
    			_out_high = _height;
    
    			for (y=0; y < _height; y++)
    			{
    				for (x=0; x < _width; x++)
    				{
    					c = _readImage[y * _width + x];
    					_outimage[3 * (y * _width + x)]     = palr[c];
    					_outimage[3 * (y * _width + x) + 1] = palg[c];
    					_outimage[3 * (y * _width + x) + 2] = palb[c];
    				}
    			}
    		}
    #if !defined(ANDROID)
    	}
    	catch (const char* e)
    	{
    		printf("Error!! %s\n", e);
    		return false;
    	}
    #endif
    
    	return true;
    }
    
    //
    // Matrix
    //
    bool CJpeglibExtension::Matrix(int nCount/*=3*/)
    {
    	if (!_width || !_height)
    		return false;
    
    	std::string szExtension;
    
    	//
    	// Find extension
    	//
    	szExtension = strrchr(_szFileName.c_str(), '.');
    
    	//
    	// Checking extension
    	//
    	if (szExtension != ".jpg" && szExtension != ".jpeg")
    		return false;
    
    	int nWidth, nHeight;
    
    	if (!IsDivide(nCount, nWidth, nHeight))
    	{
    		ASSERT(0);
    	}
    
    	nWidth  = static_cast<int>(_width / nCount);
    	nHeight = static_cast<int>(_height / nCount);
    
    	printf("resize Information Width: %d, height: %d\n", nWidth, nHeight);
    
    	register int x;
    	long y;
    	int c;
    
    #if !defined(ANDROID)
    	try
    	{
    #endif
    		if (_readImage == NULL)
    #if !defined(ANDROID)
    			throw "Please read file!!";
    #else
    			ASSERT(0);
    #endif
    
    		byte* palr = GetPalette();
    		byte* palg = GetPalette() + 256;
    		byte* palb = GetPalette() + 512;
    
    		if (_outimage)
    			free(_outimage);
    
    		_out_wide = nWidth;
    		_out_high = nHeight;
    		_outimage = (byte*)calloc(1, (nWidth * nHeight * 3));
    
    		if (_outimage == NULL)
    #if !defined(ANDROID)
    			throw "Out of memory!!";
    #else
    			ASSERT(0);
    #endif
    
    		int nImageCount = 1;
    		int nXPos = 0, nYPos = 0, nPos = 0;
    
    		//
    		// Matrix 형태로 이미지를 자르기 위해서 몇등분을 할지 입력값을 받은 다음
    		// 해당 개수 만큼 가로/세로 로 자르게 된다.
    		//
    		for (int h=1; h<=nCount; ++h)
    		{
    			for (int w=1; w<=nCount; ++w)
    			{
    				//
    				// Initialize Memory
    				//
    				for (x=0; x<(nWidth * nHeight * 3); x++)
    				{
    					_outimage[x] = GRAY;
    				}
    
    				printf("Image Filling Area(xPos:%d, yPos:%d)\n", nXPos, nYPos);
    
    				//
    				// Fill Memory
    				//
    				for (y=nYPos; y < (nHeight * h); ++y)
    				{
    					for (x=nXPos; x < (nWidth * w); ++x)
    					{
    						c = _readImage[y * _width + x];
    
    						nPos = (y - nYPos) * nWidth + (x - nXPos);
    
    						_outimage[3 * nPos]     = palr[c];
    						_outimage[3 * nPos + 1] = palg[c];
    						_outimage[3 * nPos + 2] = palb[c];
    					}
    				}
    
    				std::string szFileName;
    				char szNumber[8] = {0};
    
    				//
    				// Name setting
    				//
    				sprintf(szNumber, "%03d", nImageCount);
    				szFileName  = _szFileName.substr(0, _szFileName.size() - szExtension.size());
    				szFileName += "_split";
    				szFileName += szNumber;
    				szFileName += szExtension;
    
    				++nImageCount;
    
    				//
    				// Write Jpeg file
    				//
    				Write(szFileName.c_str(), 70, true);
    
    				nXPos += nWidth;
    			}
    
    			nXPos  = 0;
    			nYPos += nHeight;
    		}
    
    		FreeMemory();
    
    #if !defined(ANDROID)
    	}
    	catch (const char* e)
    	{
    		printf("ERROR : %s\n", e);
    
    		return false;
    	}
    #endif
    
    	return true;
    }
    
    
    void CJpeglibExtension::FreeMemory(bool bMatrix/*=false*/)
    {
    	if (bMatrix)
    		return ;
    
    	if (_outimage != NULL)
    	{
    		free(_outimage);
    	}
    
    	if (_readImage != NULL)
    	{
    		free(_readImage);
    	}
    
    	if (_palette != NULL)
    	{
    		free(_palette);
    	}
    
    	_outimage   = NULL;
    	_readImage  = NULL;
    	_palette    = NULL;
    }
    
    
    //
    // IsDivide
    //
    bool CJpeglibExtension::IsDivide(int nDivCount, int& nX, int& nY)
    {
    	//LOG1("JpeglibExtension::IsDivide\n");
    
    	if (_width == 0 || _height == 0)
    		return false;
    
    	nX = _width % nDivCount;
    	nY = _height % nDivCount;
    
    	printf("Image Information Width: %d, Height: %d\n", _width, _height);
    	printf("Image Divide Value : w:%d, h:%d\n", nX, nY);
    
    	bool bRes = true;
    
    	if (nX != 0)
    	{
    		nX = (int(_width / nDivCount) + 1) * nDivCount;
    
    		printf("Image New Size Value : w:%d\n", nX);
    
    		bRes = false;
    	}
    
    	if (nY != 0)
    	{
    		nY = (int(_height / nDivCount) + 1) * nDivCount;
    
    		printf("Image New Size Value : h:%d\n", nY);
    
    		bRes = false;
    	}
    
    	return bRes;
    }
    

    Comment


    입력하3 1498429271



    Locations of visitors to this page