mirror of
https://github.com/arsenetar/dupeguru.git
synced 2025-03-10 13:44:37 +00:00
Converted PictureBlocks to a Python extension and created a "common" C unit for common code among extensions.
This commit is contained in:
parent
0b9d936317
commit
352a21acaa
@ -1,19 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
|
|
||||||
@interface PictureBlocks : NSObject {
|
|
||||||
}
|
|
||||||
+ (NSString *)getBlocksFromImagePath:(NSString *)imagePath blockCount:(NSNumber *)blockCount;
|
|
||||||
+ (NSSize)getImageSize:(NSString *)imagePath;
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
NSString* GetBlocks(NSString *filePath, int blockCount);
|
|
@ -1,146 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import "PictureBlocks.h"
|
|
||||||
#import "Utils.h"
|
|
||||||
|
|
||||||
@implementation PictureBlocks
|
|
||||||
+ (NSString *)getBlocksFromImagePath:(NSString *)imagePath blockCount:(NSNumber *)blockCount
|
|
||||||
{
|
|
||||||
return GetBlocks(imagePath, n2i(blockCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSSize)getImageSize:(NSString *)imagePath
|
|
||||||
{
|
|
||||||
CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)imagePath, kCFURLPOSIXPathStyle, FALSE);
|
|
||||||
CGImageSourceRef source = CGImageSourceCreateWithURL(fileURL, NULL);
|
|
||||||
if (source == NULL)
|
|
||||||
return NSMakeSize(0, 0);
|
|
||||||
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
|
|
||||||
if (image == NULL)
|
|
||||||
return NSMakeSize(0, 0);
|
|
||||||
size_t width = CGImageGetWidth(image);
|
|
||||||
size_t height = CGImageGetHeight(image);
|
|
||||||
CGImageRelease(image);
|
|
||||||
CFRelease(source);
|
|
||||||
CFRelease(fileURL);
|
|
||||||
return NSMakeSize(width, height);
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
CGContextRef MyCreateBitmapContext (int width, int height)
|
|
||||||
{
|
|
||||||
CGContextRef context = NULL;
|
|
||||||
CGColorSpaceRef colorSpace;
|
|
||||||
void * bitmapData;
|
|
||||||
int bitmapByteCount;
|
|
||||||
int bitmapBytesPerRow;
|
|
||||||
|
|
||||||
bitmapBytesPerRow = (width * 4);
|
|
||||||
bitmapByteCount = (bitmapBytesPerRow * height);
|
|
||||||
|
|
||||||
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
|
|
||||||
|
|
||||||
// calloc() must be used to allocate bitmapData here because the buffer has to be zeroed.
|
|
||||||
// If it's not zeroes, when images with transparency are drawn in the context, this buffer
|
|
||||||
// will stay with undefined pixels, which means that two pictures with the same pixels will
|
|
||||||
// most likely have different blocks (which is not supposed to happen).
|
|
||||||
bitmapData = calloc(bitmapByteCount, 1);
|
|
||||||
if (bitmapData == NULL)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "Memory not allocated!");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
context = CGBitmapContextCreate (bitmapData,width,height,8,bitmapBytesPerRow,colorSpace,kCGImageAlphaNoneSkipLast);
|
|
||||||
if (context== NULL)
|
|
||||||
{
|
|
||||||
free (bitmapData);
|
|
||||||
fprintf (stderr, "Context not created!");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
CGColorSpaceRelease( colorSpace );
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns 0x00RRGGBB
|
|
||||||
int GetBlock(unsigned char *imageData, int imageWidth, int imageHeight, int boxX, int boxY, int boxW, int boxH)
|
|
||||||
{
|
|
||||||
int i,j;
|
|
||||||
int totalR = 0;
|
|
||||||
int totalG = 0;
|
|
||||||
int totalB = 0;
|
|
||||||
for(i = boxY; i < boxY + boxH; i++)
|
|
||||||
{
|
|
||||||
for(j = boxX; j < boxX + boxW; j++)
|
|
||||||
{
|
|
||||||
int offset = (i * imageWidth * 4) + (j * 4);
|
|
||||||
totalR += *(imageData + offset);
|
|
||||||
totalG += *(imageData + offset + 1);
|
|
||||||
totalB += *(imageData + offset + 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int pixelCount = boxH * boxW;
|
|
||||||
int result = 0;
|
|
||||||
result += (totalR / pixelCount) << 16;
|
|
||||||
result += (totalG / pixelCount) << 8;
|
|
||||||
result += (totalB / pixelCount);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString* GetBlocks (NSString* filePath, int blockCount)
|
|
||||||
{
|
|
||||||
CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)filePath, kCFURLPOSIXPathStyle, FALSE);
|
|
||||||
CGImageSourceRef source = CGImageSourceCreateWithURL(fileURL, NULL);
|
|
||||||
if (source == NULL)
|
|
||||||
return NULL;
|
|
||||||
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
|
|
||||||
if (image == NULL)
|
|
||||||
return NULL;
|
|
||||||
size_t width = CGImageGetWidth(image);
|
|
||||||
size_t height = CGImageGetHeight(image);
|
|
||||||
CGContextRef myContext = MyCreateBitmapContext(width, height);
|
|
||||||
CGRect myBoundingBox = CGRectMake (0, 0, width, height);
|
|
||||||
CGContextDrawImage(myContext, myBoundingBox, image);
|
|
||||||
unsigned char *bitmapData = CGBitmapContextGetData(myContext);
|
|
||||||
if (bitmapData == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
int blockHeight = height / blockCount;
|
|
||||||
if (blockHeight < 1)
|
|
||||||
blockHeight = 1;
|
|
||||||
int blockWidth = width / blockCount;
|
|
||||||
if (blockWidth < 1)
|
|
||||||
blockWidth = 1;
|
|
||||||
//blockCount might have changed
|
|
||||||
int blockXCount = (width / blockWidth);
|
|
||||||
int blockYCount = (height / blockHeight);
|
|
||||||
|
|
||||||
CFMutableArrayRef blocks = CFArrayCreateMutable(NULL, blockXCount * blockYCount, &kCFTypeArrayCallBacks);
|
|
||||||
int i,j;
|
|
||||||
for(i = 0; i < blockYCount; i++)
|
|
||||||
{
|
|
||||||
for(j = 0; j < blockXCount; j++)
|
|
||||||
{
|
|
||||||
int block = GetBlock(bitmapData, width, height, j * blockWidth, i * blockHeight, blockWidth, blockHeight);
|
|
||||||
CFStringRef strBlock = CFStringCreateWithFormat(NULL, NULL, CFSTR("%06x"), block);
|
|
||||||
CFArrayAppendValue(blocks, strBlock);
|
|
||||||
CFRelease(strBlock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CGContextRelease (myContext);
|
|
||||||
if (bitmapData) free(bitmapData);
|
|
||||||
CGImageRelease(image);
|
|
||||||
CFRelease(source);
|
|
||||||
CFRelease(fileURL);
|
|
||||||
|
|
||||||
CFStringRef result = CFStringCreateByCombiningStrings(NULL, blocks, CFSTR(""));
|
|
||||||
CFRelease(blocks);
|
|
||||||
return (NSString *)result;
|
|
||||||
}
|
|
@ -12,7 +12,6 @@
|
|||||||
CE031751109B340A00517EE6 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE031750109B340A00517EE6 /* Preferences.xib */; };
|
CE031751109B340A00517EE6 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE031750109B340A00517EE6 /* Preferences.xib */; };
|
||||||
CE031754109B345200517EE6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE031753109B345200517EE6 /* MainMenu.xib */; };
|
CE031754109B345200517EE6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE031753109B345200517EE6 /* MainMenu.xib */; };
|
||||||
CE073F6309CAE1A3005C1D2F /* dupeguru_pe_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_pe_help */; };
|
CE073F6309CAE1A3005C1D2F /* dupeguru_pe_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_pe_help */; };
|
||||||
CE0C46AA0FA0647E000BE99B /* PictureBlocks.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0C46A90FA0647E000BE99B /* PictureBlocks.m */; };
|
|
||||||
CE15C8A80ADEB8B50061D4A5 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; };
|
CE15C8A80ADEB8B50061D4A5 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; };
|
||||||
CE15C8C00ADEB8D40061D4A5 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; };
|
CE15C8C00ADEB8D40061D4A5 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; };
|
||||||
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
|
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
|
||||||
@ -77,8 +76,6 @@
|
|||||||
CE031750109B340A00517EE6 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; };
|
CE031750109B340A00517EE6 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; };
|
||||||
CE031753109B345200517EE6 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; };
|
CE031753109B345200517EE6 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; };
|
||||||
CE073F5409CAE1A3005C1D2F /* dupeguru_pe_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_pe_help; path = ../../help_pe/dupeguru_pe_help; sourceTree = SOURCE_ROOT; };
|
CE073F5409CAE1A3005C1D2F /* dupeguru_pe_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_pe_help; path = ../../help_pe/dupeguru_pe_help; sourceTree = SOURCE_ROOT; };
|
||||||
CE0C46A80FA0647E000BE99B /* PictureBlocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PictureBlocks.h; sourceTree = "<group>"; };
|
|
||||||
CE0C46A90FA0647E000BE99B /* PictureBlocks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PictureBlocks.m; sourceTree = "<group>"; };
|
|
||||||
CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
|
CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
|
||||||
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
|
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
||||||
@ -155,8 +152,6 @@
|
|||||||
080E96DDFE201D6D7F000001 /* Classes */ = {
|
080E96DDFE201D6D7F000001 /* Classes */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
CE0C46A80FA0647E000BE99B /* PictureBlocks.h */,
|
|
||||||
CE0C46A90FA0647E000BE99B /* PictureBlocks.m */,
|
|
||||||
CE381C9509914ACE003581CE /* AppDelegate.h */,
|
CE381C9509914ACE003581CE /* AppDelegate.h */,
|
||||||
CE381C9409914ACE003581CE /* AppDelegate.m */,
|
CE381C9409914ACE003581CE /* AppDelegate.m */,
|
||||||
CE848A1809DD85810004CB44 /* Consts.h */,
|
CE848A1809DD85810004CB44 /* Consts.h */,
|
||||||
@ -409,7 +404,6 @@
|
|||||||
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */,
|
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */,
|
||||||
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */,
|
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */,
|
||||||
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */,
|
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */,
|
||||||
CE0C46AA0FA0647E000BE99B /* PictureBlocks.m in Sources */,
|
|
||||||
CE80DB2E0FC192D60086DCA6 /* Dialogs.m in Sources */,
|
CE80DB2E0FC192D60086DCA6 /* Dialogs.m in Sources */,
|
||||||
CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */,
|
CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */,
|
||||||
CE80DB300FC192D60086DCA6 /* Outline.m in Sources */,
|
CE80DB300FC192D60086DCA6 /* Outline.m in Sources */,
|
||||||
|
@ -11,7 +11,7 @@ import logging
|
|||||||
import plistlib
|
import plistlib
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from Foundation import NSBundle, NSUserDefaults, NSURL
|
from Foundation import NSUserDefaults, NSURL
|
||||||
from appscript import app, k, CommandError
|
from appscript import app, k, CommandError
|
||||||
|
|
||||||
from hsutil import io
|
from hsutil import io
|
||||||
@ -21,14 +21,9 @@ from hsutil.cocoa import as_fetch
|
|||||||
|
|
||||||
from core import fs
|
from core import fs
|
||||||
from core import app_cocoa, directories
|
from core import app_cocoa, directories
|
||||||
from . import data
|
from . import data, _block_osx
|
||||||
from .cache import string_to_colors
|
|
||||||
from .scanner import ScannerPE
|
from .scanner import ScannerPE
|
||||||
|
|
||||||
mainBundle = NSBundle.mainBundle()
|
|
||||||
PictureBlocks = mainBundle.classNamed_('PictureBlocks')
|
|
||||||
assert PictureBlocks is not None
|
|
||||||
|
|
||||||
class Photo(fs.File):
|
class Photo(fs.File):
|
||||||
INITIAL_INFO = fs.File.INITIAL_INFO.copy()
|
INITIAL_INFO = fs.File.INITIAL_INFO.copy()
|
||||||
INITIAL_INFO.update({
|
INITIAL_INFO.update({
|
||||||
@ -43,17 +38,16 @@ class Photo(fs.File):
|
|||||||
def _read_info(self, field):
|
def _read_info(self, field):
|
||||||
fs.File._read_info(self, field)
|
fs.File._read_info(self, field)
|
||||||
if field == 'dimensions':
|
if field == 'dimensions':
|
||||||
size = PictureBlocks.getImageSize_(unicode(self.path))
|
self.dimensions = _block_osx.get_image_size(unicode(self.path))
|
||||||
self.dimensions = (size.width, size.height)
|
|
||||||
|
|
||||||
def get_blocks(self, block_count_per_side):
|
def get_blocks(self, block_count_per_side):
|
||||||
try:
|
try:
|
||||||
blocks = PictureBlocks.getBlocksFromImagePath_blockCount_(unicode(self.path), block_count_per_side)
|
blocks = _block_osx.getblocks(unicode(self.path), block_count_per_side)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise IOError('The reading of "%s" failed with "%s"' % (unicode(self.path), unicode(e)))
|
raise IOError('The reading of "%s" failed with "%s"' % (unicode(self.path), unicode(e)))
|
||||||
if not blocks:
|
if not blocks:
|
||||||
raise IOError('The picture %s could not be read' % unicode(self.path))
|
raise IOError('The picture %s could not be read' % unicode(self.path))
|
||||||
return string_to_colors(blocks)
|
return blocks
|
||||||
|
|
||||||
|
|
||||||
class IPhoto(Photo):
|
class IPhoto(Photo):
|
||||||
|
@ -23,5 +23,6 @@ os.system('python setup.py build_ext --inplace')
|
|||||||
os.chdir('..')
|
os.chdir('..')
|
||||||
move(op.join('modules', '_block.so'), '_block.so')
|
move(op.join('modules', '_block.so'), '_block.so')
|
||||||
move(op.join('modules', '_block.pyd'), '_block.pyd')
|
move(op.join('modules', '_block.pyd'), '_block.pyd')
|
||||||
|
move(op.join('modules', '_block_osx.so'), '_block_osx.so')
|
||||||
move(op.join('modules', '_cache.so'), '_cache.so')
|
move(op.join('modules', '_cache.so'), '_cache.so')
|
||||||
move(op.join('modules', '_cache.pyd'), '_cache.pyd')
|
move(op.join('modules', '_cache.pyd'), '_cache.pyd')
|
||||||
|
@ -7,59 +7,17 @@
|
|||||||
* http://www.hardcoded.net/licenses/hs_license
|
* http://www.hardcoded.net/licenses/hs_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define PY_SSIZE_T_CLEAN
|
#include "common.h"
|
||||||
#include "Python.h"
|
|
||||||
|
|
||||||
/* avgdiff/maxdiff has been called with empty lists */
|
/* avgdiff/maxdiff has been called with empty lists */
|
||||||
static PyObject *NoBlocksError;
|
static PyObject *NoBlocksError;
|
||||||
/* avgdiff/maxdiff has been called with 2 block lists of different size. */
|
/* avgdiff/maxdiff has been called with 2 block lists of different size. */
|
||||||
static PyObject *DifferentBlockCountError;
|
static PyObject *DifferentBlockCountError;
|
||||||
|
|
||||||
/* It seems like MS VC defines min/max already */
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
static int
|
|
||||||
max(int a, int b)
|
|
||||||
{
|
|
||||||
return b > a ? b : a;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
min(int a, int b)
|
|
||||||
{
|
|
||||||
return b < a ? b : a;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Create a tuple out of an array of integers. */
|
|
||||||
static PyObject*
|
|
||||||
inttuple(int n, ...)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
PyObject *pnumber;
|
|
||||||
PyObject *result;
|
|
||||||
va_list numbers;
|
|
||||||
|
|
||||||
va_start(numbers, n);
|
|
||||||
result = PyTuple_New(n);
|
|
||||||
|
|
||||||
for (i=0; i<n; i++) {
|
|
||||||
pnumber = PyInt_FromLong(va_arg(numbers, int));
|
|
||||||
if (pnumber == NULL) {
|
|
||||||
Py_DECREF(result);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
PyTuple_SET_ITEM(result, i, pnumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
va_end(numbers);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns a 3 sized tuple containing the mean color of 'image'.
|
/* Returns a 3 sized tuple containing the mean color of 'image'.
|
||||||
* image: a PIL image or crop.
|
* image: a PIL image or crop.
|
||||||
*/
|
*/
|
||||||
static PyObject*
|
static PyObject* getblock(PyObject *image)
|
||||||
getblock(PyObject *image)
|
|
||||||
{
|
{
|
||||||
int i, totr, totg, totb;
|
int i, totr, totg, totb;
|
||||||
Py_ssize_t pixel_count;
|
Py_ssize_t pixel_count;
|
||||||
@ -107,8 +65,7 @@ getblock(PyObject *image)
|
|||||||
/* Returns the difference between the first block and the second.
|
/* Returns the difference between the first block and the second.
|
||||||
* It returns an absolute sum of the 3 differences (RGB).
|
* It returns an absolute sum of the 3 differences (RGB).
|
||||||
*/
|
*/
|
||||||
static int
|
static int diff(PyObject *first, PyObject *second)
|
||||||
diff(PyObject *first, PyObject *second)
|
|
||||||
{
|
{
|
||||||
Py_ssize_t r1, g1, b1, r2, b2, g2;
|
Py_ssize_t r1, g1, b1, r2, b2, g2;
|
||||||
PyObject *pr, *pg, *pb;
|
PyObject *pr, *pg, *pb;
|
||||||
@ -144,8 +101,7 @@ If it is 10, for example, 100 blocks will be returns (10 width, 10 height). The
|
|||||||
necessarely cover square areas. The area covered by each block will be proportional to the image\n\
|
necessarely cover square areas. The area covered by each block will be proportional to the image\n\
|
||||||
itself.\n");
|
itself.\n");
|
||||||
|
|
||||||
static PyObject*
|
static PyObject* block_getblocks2(PyObject *self, PyObject *args)
|
||||||
block_getblocks2(PyObject *self, PyObject *args)
|
|
||||||
{
|
{
|
||||||
int block_count_per_side, width, height, block_width, block_height, ih;
|
int block_count_per_side, width, height, block_width, block_height, ih;
|
||||||
PyObject *image;
|
PyObject *image;
|
||||||
@ -218,8 +174,7 @@ PyDoc_STRVAR(block_avgdiff_doc,
|
|||||||
If the result surpasses limit, limit + 1 is returned, except if less than min_iterations\n\
|
If the result surpasses limit, limit + 1 is returned, except if less than min_iterations\n\
|
||||||
iterations have been made in the blocks.\n");
|
iterations have been made in the blocks.\n");
|
||||||
|
|
||||||
static PyObject*
|
static PyObject* block_avgdiff(PyObject *self, PyObject *args)
|
||||||
block_avgdiff(PyObject *self, PyObject *args)
|
|
||||||
{
|
{
|
||||||
PyObject *first, *second;
|
PyObject *first, *second;
|
||||||
int limit, min_iterations;
|
int limit, min_iterations;
|
||||||
|
229
core_pe/modules/block_osx.m
Normal file
229
core_pe/modules/block_osx.m
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
/* Created By: Virgil Dupras
|
||||||
|
* Created On: 2010-02-04
|
||||||
|
* Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
*
|
||||||
|
* This software is licensed under the "HS" License as described in the "LICENSE" file,
|
||||||
|
* which should be included with this package. The terms are also available at
|
||||||
|
* http://www.hardcoded.net/licenses/hs_license
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
static CFStringRef
|
||||||
|
pystring2cfstring(PyObject *pystring)
|
||||||
|
{
|
||||||
|
PyObject *encoded;
|
||||||
|
UInt8 *s;
|
||||||
|
CFIndex size;
|
||||||
|
CFStringRef result;
|
||||||
|
|
||||||
|
if (PyUnicode_Check(pystring)) {
|
||||||
|
encoded = PyUnicode_AsUTF8String(pystring);
|
||||||
|
if (encoded == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
encoded = pystring;
|
||||||
|
Py_INCREF(encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
s = (UInt8*)PyString_AS_STRING(encoded);
|
||||||
|
size = PyString_GET_SIZE(encoded);
|
||||||
|
result = CFStringCreateWithBytes(NULL, s, size, kCFStringEncodingUTF8, FALSE);
|
||||||
|
Py_DECREF(encoded);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject* block_osx_get_image_size(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *path;
|
||||||
|
CFStringRef image_path;
|
||||||
|
CFURLRef image_url;
|
||||||
|
CGImageSourceRef source;
|
||||||
|
CGImageRef image;
|
||||||
|
size_t width, height;
|
||||||
|
PyObject *pwidth, *pheight;
|
||||||
|
PyObject *result;
|
||||||
|
|
||||||
|
width = 0;
|
||||||
|
height = 0;
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &path)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
image_path = pystring2cfstring(path);
|
||||||
|
if (image_path == NULL) {
|
||||||
|
return PyErr_NoMemory();
|
||||||
|
}
|
||||||
|
image_url = CFURLCreateWithFileSystemPath(NULL, image_path, kCFURLPOSIXPathStyle, FALSE);
|
||||||
|
CFRelease(image_path);
|
||||||
|
|
||||||
|
source = CGImageSourceCreateWithURL(image_url, NULL);
|
||||||
|
CFRelease(image_url);
|
||||||
|
if (source != NULL) {
|
||||||
|
image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
|
||||||
|
if (image != NULL) {
|
||||||
|
width = CGImageGetWidth(image);
|
||||||
|
height = CGImageGetHeight(image);
|
||||||
|
CGImageRelease(image);
|
||||||
|
}
|
||||||
|
CFRelease(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
pwidth = PyInt_FromSsize_t(width);
|
||||||
|
if (pwidth == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pheight = PyInt_FromSsize_t(height);
|
||||||
|
if (pheight == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
result = PyTuple_Pack(2, pwidth, pheight);
|
||||||
|
Py_DECREF(pwidth);
|
||||||
|
Py_DECREF(pheight);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CGContextRef
|
||||||
|
MyCreateBitmapContext(int width, int height)
|
||||||
|
{
|
||||||
|
CGContextRef context = NULL;
|
||||||
|
CGColorSpaceRef colorSpace;
|
||||||
|
void *bitmapData;
|
||||||
|
int bitmapByteCount;
|
||||||
|
int bitmapBytesPerRow;
|
||||||
|
|
||||||
|
bitmapBytesPerRow = (width * 4);
|
||||||
|
bitmapByteCount = (bitmapBytesPerRow * height);
|
||||||
|
|
||||||
|
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
|
||||||
|
|
||||||
|
// calloc() must be used to allocate bitmapData here because the buffer has to be zeroed.
|
||||||
|
// If it's not zeroes, when images with transparency are drawn in the context, this buffer
|
||||||
|
// will stay with undefined pixels, which means that two pictures with the same pixels will
|
||||||
|
// most likely have different blocks (which is not supposed to happen).
|
||||||
|
bitmapData = calloc(bitmapByteCount, 1);
|
||||||
|
if (bitmapData == NULL) {
|
||||||
|
fprintf(stderr, "Memory not allocated!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
context = CGBitmapContextCreate(bitmapData, width, height, 8, bitmapBytesPerRow, colorSpace,
|
||||||
|
kCGImageAlphaNoneSkipLast);
|
||||||
|
if (context== NULL) {
|
||||||
|
free(bitmapData);
|
||||||
|
fprintf(stderr, "Context not created!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
CGColorSpaceRelease(colorSpace);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject* getblock(unsigned char *imageData, int imageWidth, int imageHeight, int boxX, int boxY, int boxW, int boxH)
|
||||||
|
{
|
||||||
|
int i,j, totalR, totalG, totalB;
|
||||||
|
|
||||||
|
totalR = totalG = totalB = 0;
|
||||||
|
for(i=boxY; i<boxY+boxH; i++) {
|
||||||
|
for(j=boxX; j<boxX+boxW; j++) {
|
||||||
|
int offset = (i * imageWidth * 4) + (j * 4);
|
||||||
|
totalR += *(imageData + offset);
|
||||||
|
totalG += *(imageData + offset + 1);
|
||||||
|
totalB += *(imageData + offset + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int pixelCount = boxH * boxW;
|
||||||
|
totalR /= pixelCount;
|
||||||
|
totalG /= pixelCount;
|
||||||
|
totalB /= pixelCount;
|
||||||
|
|
||||||
|
return inttuple(3, totalR, totalG, totalB);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject* block_osx_getblocks(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *path, *result;
|
||||||
|
CFStringRef image_path;
|
||||||
|
CFURLRef image_url;
|
||||||
|
CGImageSourceRef source;
|
||||||
|
CGImageRef image;
|
||||||
|
size_t width, height;
|
||||||
|
int block_count, block_width, block_height, block_xcount, block_ycount, i;
|
||||||
|
|
||||||
|
width = 0;
|
||||||
|
height = 0;
|
||||||
|
if (!PyArg_ParseTuple(args, "Oi", &path, &block_count)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
image_path = pystring2cfstring(path);
|
||||||
|
if (image_path == NULL) {
|
||||||
|
return PyErr_NoMemory();
|
||||||
|
}
|
||||||
|
image_url = CFURLCreateWithFileSystemPath(NULL, image_path, kCFURLPOSIXPathStyle, FALSE);
|
||||||
|
CFRelease(image_path);
|
||||||
|
|
||||||
|
source = CGImageSourceCreateWithURL(image_url, NULL);
|
||||||
|
CFRelease(image_url);
|
||||||
|
if (source == NULL) {
|
||||||
|
return PyErr_NoMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
|
||||||
|
if (image == NULL) {
|
||||||
|
CFRelease(source);
|
||||||
|
return PyErr_NoMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
width = CGImageGetWidth(image);
|
||||||
|
height = CGImageGetHeight(image);
|
||||||
|
CGContextRef myContext = MyCreateBitmapContext(width, height);
|
||||||
|
CGRect myBoundingBox = CGRectMake(0, 0, width, height);
|
||||||
|
CGContextDrawImage(myContext, myBoundingBox, image);
|
||||||
|
unsigned char *bitmapData = CGBitmapContextGetData(myContext);
|
||||||
|
CGContextRelease(myContext);
|
||||||
|
CGImageRelease(image);
|
||||||
|
CFRelease(source);
|
||||||
|
if (bitmapData == NULL) {
|
||||||
|
return PyErr_NoMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
block_width = max(width/block_count, 1);
|
||||||
|
block_height = max(height/block_count, 1);
|
||||||
|
/* block_count might have changed */
|
||||||
|
block_xcount = (width / block_width);
|
||||||
|
block_ycount = (height / block_height);
|
||||||
|
|
||||||
|
result = PyList_New(block_xcount * block_ycount);
|
||||||
|
if (result == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0; i<block_ycount; i++)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
for(j=0; j<block_xcount; j++)
|
||||||
|
{
|
||||||
|
PyObject *block = getblock(bitmapData, width, height, j*block_width, i*block_height,
|
||||||
|
block_width, block_height);
|
||||||
|
PyList_SET_ITEM(result, i*block_ycount+j, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(bitmapData);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef BlockOsxMethods[] = {
|
||||||
|
{"get_image_size", block_osx_get_image_size, METH_VARARGS, ""},
|
||||||
|
{"getblocks", block_osx_getblocks, METH_VARARGS, ""},
|
||||||
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
PyMODINIT_FUNC
|
||||||
|
init_block_osx(void)
|
||||||
|
{
|
||||||
|
Py_InitModule("_block_osx", BlockOsxMethods);
|
||||||
|
}
|
@ -6,8 +6,8 @@
|
|||||||
* which should be included with this package. The terms are also available at
|
* which should be included with this package. The terms are also available at
|
||||||
* http://www.hardcoded.net/licenses/hs_license
|
* http://www.hardcoded.net/licenses/hs_license
|
||||||
*/
|
*/
|
||||||
#define PY_SSIZE_T_CLEAN
|
|
||||||
#include "Python.h"
|
#include "common.h"
|
||||||
|
|
||||||
/* I know that there strtol out there, but it requires a pointer to
|
/* I know that there strtol out there, but it requires a pointer to
|
||||||
* a char, which would in turn require me to buffer my chars around,
|
* a char, which would in turn require me to buffer my chars around,
|
||||||
@ -49,24 +49,13 @@ cache_string_to_colors(PyObject *self, PyObject *args)
|
|||||||
long r, g, b;
|
long r, g, b;
|
||||||
Py_ssize_t ci;
|
Py_ssize_t ci;
|
||||||
PyObject *color_tuple;
|
PyObject *color_tuple;
|
||||||
PyObject *pr, *pg, *pb;
|
|
||||||
|
|
||||||
ci = i * 6;
|
ci = i * 6;
|
||||||
r = (xchar_to_long(s[ci]) << 4) + xchar_to_long(s[ci+1]);
|
r = (xchar_to_long(s[ci]) << 4) + xchar_to_long(s[ci+1]);
|
||||||
g = (xchar_to_long(s[ci+2]) << 4) + xchar_to_long(s[ci+3]);
|
g = (xchar_to_long(s[ci+2]) << 4) + xchar_to_long(s[ci+3]);
|
||||||
b = (xchar_to_long(s[ci+4]) << 4) + xchar_to_long(s[ci+5]);
|
b = (xchar_to_long(s[ci+4]) << 4) + xchar_to_long(s[ci+5]);
|
||||||
|
|
||||||
pr = PyInt_FromLong(r);
|
color_tuple = inttuple(3, r, g, b);
|
||||||
pg = PyInt_FromLong(g);
|
|
||||||
pb = PyInt_FromLong(b);
|
|
||||||
if (pb == NULL) {
|
|
||||||
Py_DECREF(result);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
color_tuple = PyTuple_Pack(3, pr, pg, pb);
|
|
||||||
Py_DECREF(pr);
|
|
||||||
Py_DECREF(pg);
|
|
||||||
Py_DECREF(pb);
|
|
||||||
if (color_tuple == NULL) {
|
if (color_tuple == NULL) {
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
45
core_pe/modules/common.c
Normal file
45
core_pe/modules/common.c
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/* Created By: Virgil Dupras
|
||||||
|
* Created On: 2010-02-04
|
||||||
|
* Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
*
|
||||||
|
* This software is licensed under the "HS" License as described in the "LICENSE" file,
|
||||||
|
* which should be included with this package. The terms are also available at
|
||||||
|
* http://www.hardcoded.net/licenses/hs_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
int max(int a, int b)
|
||||||
|
{
|
||||||
|
return b > a ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
int min(int a, int b)
|
||||||
|
{
|
||||||
|
return b < a ? b : a;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PyObject* inttuple(int n, ...)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
PyObject *pnumber;
|
||||||
|
PyObject *result;
|
||||||
|
va_list numbers;
|
||||||
|
|
||||||
|
va_start(numbers, n);
|
||||||
|
result = PyTuple_New(n);
|
||||||
|
|
||||||
|
for (i=0; i<n; i++) {
|
||||||
|
pnumber = PyInt_FromLong(va_arg(numbers, int));
|
||||||
|
if (pnumber == NULL) {
|
||||||
|
Py_DECREF(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyTuple_SET_ITEM(result, i, pnumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(numbers);
|
||||||
|
return result;
|
||||||
|
}
|
20
core_pe/modules/common.h
Normal file
20
core_pe/modules/common.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/* Created By: Virgil Dupras
|
||||||
|
* Created On: 2010-02-04
|
||||||
|
* Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
*
|
||||||
|
* This software is licensed under the "HS" License as described in the "LICENSE" file,
|
||||||
|
* which should be included with this package. The terms are also available at
|
||||||
|
* http://www.hardcoded.net/licenses/hs_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#include "Python.h"
|
||||||
|
|
||||||
|
/* It seems like MS VC defines min/max already */
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
int max(int a, int b);
|
||||||
|
int min(int a, int b);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Create a tuple out of an array of integers. */
|
||||||
|
PyObject* inttuple(int n, ...);
|
@ -6,12 +6,25 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
from distutils.extension import Extension
|
from distutils.extension import Extension
|
||||||
|
|
||||||
|
exts = []
|
||||||
|
|
||||||
|
exts.append(Extension("_block", ["block.c", "common.c"]))
|
||||||
|
exts.append(Extension("_cache", ["cache.c", "common.c"]))
|
||||||
|
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
exts.append(Extension(
|
||||||
|
"_block_osx", ["block_osx.m", "common.c"],
|
||||||
|
extra_link_args=[
|
||||||
|
"-framework", "CoreFoundation",
|
||||||
|
"-framework", "Foundation",
|
||||||
|
"-framework", "ApplicationServices",
|
||||||
|
]))
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
ext_modules = [
|
ext_modules = exts,
|
||||||
Extension("_block", ["block.c"]),
|
|
||||||
Extension("_cache", ["cache.c"]),
|
|
||||||
]
|
|
||||||
)
|
)
|
Loading…
x
Reference in New Issue
Block a user