Implementing a CIFilter in a NSView/Mac OS X Xojo rectcontrol doesn’t seem to be as complicated as I always thought.
But something bothers me:
There’s the filterNamesInCategories method which, when called with a NIL ptr, gives me all the names of available filters.
I can even get their localized names.
But then there’s the
Protected Shared Function getFilterNamesinCategory(id as ptr, category as cfstringref) As Ptr
#if targetmacos
declare function filterNamesInCategory lib appkit selector "filterNamesInCategory:" (id as ptr, categorie as cfstringref) as ptr
return filterNamesInCategory (id, category)
#endif
End Function
which I call in a method like
Shared Function BuiltInFilters() As text()
dim myfilters as ptr = getFilterNamesinCategory (classptr, CIFBuiltIn)
return myfilters.totextarray
End Function
with
Protected Const CIFBuiltIn As String = "kCICategoryBuiltIn"
This method returns a ptr, supposed to be a NSArray of NSStrings, but when I cast a count method on that, this property always returns 0.
Cannot be or can it? Did I do something wrong?
kCICategoryBuiltIn is the constant name, not the value. Ideally you should be querying the OS for the value of the constant. Joe R has posted several different variants of code for getting the value from the OS. I don’t recall under exactly what topic at the moment.
Just remember that the value behind a constant can change with a OS update.
I have some Core Image tutorials planned for the xDev magazine in the future. Core Image is easy to implement, but difficult to use efficiently. Yosemite broke some previous optimization techniques
Here’s one version of Joe’s code that I use to get constant values.
[code]Function frameworkConstant(frameworkName as string, constantName as string) As Ptr #if TargetCocoa then
Declare Function dlopen Lib “System” ( path As CString, mode As Integer ) As ptr
Declare Function dlsym Lib “System” ( handle As PTr, name As CString ) As ptr
Const RTLD_LAZY = 1
Const RTLD_GLOBAL = 8
Dim libPtr As ptr = dlopen( "/System/Library/Frameworks/" + frameWorkName + ".framework/" + frameworkName, RTLD_LAZY Or RTLD_GLOBAL )
If libPtr = Nil Then return nil
return dlsym( libPtr, constantName )
I’m not sure the better method, but I’ve started using something like this…
[code]Function MacConstant(name as string, frameworkID as String) As ptr #if TargetMacOS
Declare Function CFBundleGetBundleWithIdentifier Lib "Foundation" (bundleID as CFStringRef) As ptr
Declare Function CFBundleGetDataPointerForName Lib "Foundation" (bundle as ptr, symbolName As CFStringRef) As ptr
Declare Function CFBundleLoadExecutable Lib "Foundation" (bundle as ptr) As Boolean
Declare Function CFBundleIsExecutableLoaded Lib "Foundation" (bundle as ptr) As Boolean
dim frameworkRef As ptr=CFBundleGetBundleWithIdentifier(frameworkID)
if frameworkRef=nil then Return nil
if not CFBundleIsExecutableLoaded(frameworkRef) then //bundle is not loaded
if not CFBundleLoadExecutable(frameworkRef) then //try to load it
//fail
Return nil
end if
end if
Return CFBundleGetDataPointerForName(frameworkRef,name) //lookup the constant
#endif
End Function
[/code]
You have to use the reverse domain ID of the framework ie
dim res as string = MacConstant(“kCICategoryBuiltIn”,“com.apple.CoreImage”).CFStringRef(0)
The thing that got me moving to this, is that some functions require non-string constants like kCFBooleanTrue kCFBooleanFalse. It can be done with numberWithBool, but that’s not the correct (and stable way) to do it. I really think this should be a built function. Feature request perhaps?
something like
Declare Constant kCFBooleanTrue lib "Foundation" as ptr
Thanks a lot, you all!
I very often find the really hard part with declares is to decode Apple’s docs. Probably the hint to the kConastants being placeholders for the really strings is buried somewhere in the docs, but it’s nothing that you find so easily ;(
@Sam: Glad I bought the Omegabundle. You got a reader (and I’m sure more than one).
CIFilters returning a property dictionary looks like the wisest would be to subclass the filters to make their handling Xojo-like. At least, thanks to you all, I can read the values now and will ponder a bit about comfortable handling.
(As I was asked: Yes, I will put this all on a GitHub once it is usable (it is in fact part of an own very basic MacOS Lib using the new framework and (hopefully) 64bit-aware.). Again most of the features will be included as extension modules, so CIFilters would be available for every RectControl.)
MBS has CoreImage all laid out for you, for many years I used this.
When I started to write my own, and I created a lot of CIFilter classes. How now I use a bare bones class and refer to the documents for class and property names. The reason being is that it made it easier to translate from Obj-C, reduced app size and made it more flexible.
There are some wonderful improvements in CI in Yosemite (which significantly reduce rendering time), however Yosemite introduced a new set of issues.
I would also recommend steering clear of CGImage to CIImage as some CGImages do not translate very well and make it very easy to crash your GPU, which results in some pretty pretty lights on your screen.
Thanks, Sam! I’m trying to implement this on NSView level, so in most cases Ill take the view’s content as input value for the CIFIlter.
I made it to read the value (see above, replaced the image) now “just” have to create the filterWithName:keysAndValues method to see if I can manipulate the filter too
If you’re working with NSView, consider using CALayer. You can call makeBackingLayer on the view and the view will create a CALayer for itself,then you can set the contentFilters array and any drawing will be processed through those filters. I’ve been working with that here and it is incredibly fast!
Thanks again, Jim! For the test I assigned the filter to the compositingFilter property. That’s probably the reason the border is influenced too. And yes, fast it is indeed! A high-speed image processing app in Xojo would be absolutely possible, at least with respect to filters.
EDIT: And thanks for the GetConstantName method! Works nicely!
ANOTHER EDIT: Result is the same. When I use only one filter layer, it’s ok (a bit faster too) to assign the CIFilter to the compositingfilter property. Of course stacking a few sounds very tempting too!
THIRD EDIT: Maybe the result will not be the same when I use a different background layer. So far I only use one layer for everything (+ filter)
What is your ultimate goal with CoreImage? Are you writing an image editing program? While filters in a NSView make life easy in the beginning, when you want to do more complicated processes, you’ll run into difficulty.
Yes, sure, CIFilters are not xplat. The background is that I am mostly into Mac &iOS development than the other platforms (but will try to gain more insight into them too), and I want to have a solution that is new framework & 64bit compatible and Xojo-like to handle.
I made several attempts to come up with a solution, both on iOS and OS X, and believe that I finally have found a really workable way. The CIFilters came into life because I found myself working on NSView level for the umpteeth time and desired something new in between. If I ever figure out how to send the init parameters*, there shall be something like
DIM myFilter as new MacOSCIBlurfilter (parameters)
myrectcontrol.composingfilter = myfilter
Without the initparameters, this works already.
But about them, I am still trying to figure out the object to pass.
*Would that be a Memoryblock with [once or several times] (Pointer to paramatername, NSNumber) Pointer to NIl?
The ultimate goal? One one hand purely academic; I am trying to increase my Xojo &OS knowledge and get accustomed to the new framework**. On the other hand slowly build a core MacOSLib replacement that is purely Cocoa and completely Xojo-like to handle (so there are a lot of conversion and convenience routines) and integrates into Xojo controls instead of replacing them. And on the last hand(??) finally enable me to design the OS X app I started on in Nov. 13 which I rebuilt already 3 times from scratch because after learning I couldn’t stand to look at my old code anymore. Sure, I could have finished it already with MacOSLib or Christian’s plugins, but probably 80% of what I had done would still be a mystery to me.
** I must admit I love the new framework. It’s so structured and logical in most places. I adapted its style for my project and really enjoy things like MacOSNSArray.toPtrArrayt.totext giving you a text() containing the types of pointers in a NSArray (their names if Strings, values if numbers, types if objects).
P.S.: No, I wouldn’t try to create an Image editor. Regarding pixels, I am happy with Photoshop and Perfect Photo Suite. Lot of respect for Shine!
You may know that my main profession is designing books & magazines. I am one of the many who still miss Freehand which was so much more easy to handle than Illustrator. It would really have been a challenge trying to create something (at least the core, don’t want to get into hubris) with Xojo, but I think I’ll better invest the 50 into https://affinity.serif.com/en-gb/ which looks very promising. And continue with the other project instead
Another question regarding the constants: I have made good progress and can now change some of the CIFIlter parameters.
I can, with a Method ConstantName (calling cfbundlegetdatapointerforname, thanks again!) read the real names of the constants, with FrameWorkname being “com.apple.CoreImage”
I declared an enum so I dont have to handle strings for the type and assign it in a method (excerpt):
dim cname as text = MacOSFoundation.ConstantName("kCIAttributeType", MacOSCIFilter.Frameworkname)
dim mytype as text = me.TextForKey (cname)
system.debuglog "AttributeTypeName: "+mytype
select case mytype
case MacOSFoundation.ConstantName ("kCIAttributeTypePosition3", MacOSCIFilter.frameworkname)
return ValueType.Position3D
case MacOSFoundation.ConstantName ("kCIAttributeTypeRectangle", MacOSCIFilter.frameworkname)
return ValueType.Rectangle
// case MacOSFoundation.ConstantName ("kCIAttributeTypeColor", MacOSCIFilter.frameworkname)
// return ValueType.Color
// case MacOSFoundation.ConstantName ("kCIAttributeTypeImage", MacOSCIFilter.frameworkname)
// return ValueType.Image
// case MacOSFoundation.ConstantName ("kCIAttributeTypeTransform", MacOSCIFilter.frameworkname)
// return ValueType.Transform
End Select
But this doesn’t work for the cases I have commented out. Do these constants belong to another framework than CoreImage? And if: How can I find out?