It is currently Sun Jun 30, 2024 10:57 pm


All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 43 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: use of a color curves preset in a python filter
PostPosted: Thu Jan 25, 2024 9:12 am  (#1) 
Offline
GimpChat Member
User avatar

Joined: Jan 20, 2013
Posts: 14816
Location: roma, italy
GIMP Version: 2.10.34
Operating System: Windows
GIMP Experience: Intermediate Level



- given that exist predefined color curves presets and that those can be used interactively and each preset usually contains 256 values per color
- given that exists a procedure called gimp-drawable-curves-explicit available for those who creates filters

my question is:

- does exist somewhere an example on how to:
- 1.load the 256 values (text) per color stored in one preset
- 2.convert those into the requested series of 256 float values required by the plug-in statement
- 3.create a python routine to be inserted into a python plug-in?

Thanks.

_________________
"Where am I ?"


Share on Facebook Share on Twitter Share on Orkut Share on Digg Share on MySpace Share on Delicious Share on Technorati
Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Thu Jan 25, 2024 5:16 pm  (#2) 
Offline
Script Coder
User avatar

Joined: Oct 25, 2010
Posts: 4752
None I know of but not that difficult. Things to take care of:

* Spotting the right setting
* Settings lists all 5 channels (value/red/green/blue/alpha) and you possibly want to recognize those that are identity to avoid playing them.

_________________
Image


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Thu Jan 25, 2024 10:26 pm  (#3) 
Offline
GimpChat Member
User avatar

Joined: Jan 20, 2013
Posts: 14816
Location: roma, italy
Thanks Ofnuts, for your reply.
Yes, you are right, it's not difficult, but simply ...annoying, it would have been much simpler if there was a defined (python) function which having in input the name of the preset and its location, could give in output the gimp procedure. And in practice, there is: just embedded in the online selection of a stored preset.

_________________
"Where am I ?"


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 5:19 am  (#4) 
Offline
Script Coder
User avatar

Joined: Oct 25, 2010
Posts: 4752
import os,sys,traceback,re,codecs

from itertools import islice

class Channel:
    def __init__(self,name,samples):
        self.name=str(name)  # Never unicode
        self.samples=samples
   
class Setting:
    def __init__(self,name,time,channels):
        self.name=name
        self.time=time
        self.channels=channels

def extract(pattern,line):
    matched=re.match(pattern,line.strip())
    return matched.group(1) if matched else None

def readChannel(settingsFile,channelName):
    nameLine,_,typeLine,pointsLine,pointsTypeLine,samplesCountLine,samplesLine=islice(settingsFile,7)
    actualName=extract(r'\(channel (\w+)\)',nameLine)
    if actualName!=channelName:
        raise Exception("Channel name mismatch: expected: %s, actual: %s" % (channelName,actualName))
    if pointsLine.strip()=='(points 4 0 0 1 1)': # identity for channel
        return Channel(channelName,None)
    samplesCount=int(extract(r'\(n-samples (\d+)\)',samplesCountLine))
    samplesString=extract(r'\(samples \d+ ([0-9. ]+)\)',samplesLine) # skip count value
    samples=[float(x) for x in samplesString.split()]
    if len(samples)!=samplesCount:
        raise Exception("Samples count mismatch: expected: %d, actual: %d" % (samplesCount,len(samples)))
    return Channel(channelName,samples)
   
def readSetting(settingsFile):
    try:
        nameLine,timeLine,linearLine=islice(settingsFile,3)
    except ValueError:
        return None
    settingName=extract(r'\(GimpCurvesConfig "(.+)"',nameLine)
    settingTime=int(extract(r'\(time (\d+)\)',timeLine))
    channels=[]
    for channelName in ["value","red","green","blue","alpha"]:
        channel=readChannel(settingsFile,channelName)
        if channel.samples:
            channels.append(channel) # Don't add identity channels
    return Setting(settingName,settingTime,channels)

def settingsFrom(settingsFile):
    # Skip top two lines
    _,_=islice(settingsFile,2)
    while True:
        setting=readSetting(settingsFile)
        if not setting:
            break
        yield setting

def load(settingsFilePath):
    settings=[]
    with codecs.open(settingsFilePath, 'r', encoding='utf-8') as settingsFile:
        for setting in settingsFrom(settingsFile):
            if setting.time==0: # Only named settings
                settings.append(setting)
    return settings

if __name__=="__main__":
    settings=load("./GimpCurvesConfig.settings")
   
    for s in settings:
        print "### %s" % s.name
        for c in s.channels:
            print "   %s: %d" % (c.name,len(c.samples))
            for sample in c.samples[:5]:
                print "        %7.5f" % sample


As written, only keeps settings explicitly saved with a name, and only saves channels that have been changed so you can iterate these and apply gimp-drawable-curves-explicit() on each.

The bit with "if __name__=="__main__":" is of course to test that the thing works.

_________________
Image


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 6:50 am  (#5) 
Offline
GimpChat Member
User avatar

Joined: Jan 20, 2013
Posts: 14816
Location: roma, italy
Wow, thanks a lot!
I will try to understand it as much as possible for my limited capabilities.

_________________
"Where am I ?"


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 7:26 am  (#6) 
Offline
GimpChat Member
User avatar

Joined: Jan 13, 2011
Posts: 2260
Location: Poland
apply_curve by code Tin Tran:
viewtopic.php?f=9&t=14058&start=0

apply_color_curves_preset.py by code Tin Tran:
viewtopic.php?f=9&t=14064&start

My way of using it:
https://www.gimpscripts.net/2021/10/mrq ... s-rpl.html


Attachments:
Aply curves.png
Aply curves.png [ 27.96 KiB | Viewed 661 times ]

_________________
Image

Slava
Ukraini!
Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 7:47 am  (#7) 
Offline
GimpChat Member
User avatar

Joined: Jan 20, 2013
Posts: 14816
Location: roma, italy
Thanks Marek, I'll download also yours.

_________________
"Where am I ?"


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 7:59 am  (#8) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 4000
Location: Canada
Here's my finding I got stuck trying to debug because I can't seem to export presets. Not sure why. (ofnuts code seems way cleaner so I try to use it but...)
My export1 file always has a single time 0. No matter what I do I can't get my saved named color curved presets to export to file with multiple named. it's always single and without my named saved color curve settings. Wonder if my gimp is broken with that (I am on GIMP 2.10.34)
#!/usr/bin/env python
from gimpfu import *
import os,sys,traceback,re,codecs

from itertools import islice

class Channel:
    def __init__(self,name,samples):
        self.name=str(name)  # Never unicode
        self.samples=samples
   
class Setting:
    def __init__(self,name,time,channels):
        self.name=name
        self.time=time
        self.channels=channels

def extract(pattern,line):
    matched=re.match(pattern,line.strip())
    return matched.group(1) if matched else None

def readChannel(settingsFile,channelName):
    nameLine,_,typeLine,pointsLine,pointsTypeLine,samplesCountLine,samplesLine=islice(settingsFile,7)
    actualName=extract(r'\(channel (\w+)\)',nameLine)
    if actualName!=channelName:
        raise Exception("Channel name mismatch: expected: %s, actual: %s" % (channelName,actualName))
    if pointsLine.strip()=='(points 4 0 0 1 1)': # identity for channel
        return Channel(channelName,None)
    samplesCount=int(extract(r'\(n-samples (\d+)\)',samplesCountLine))
    samplesString=extract(r'\(samples \d+ ([0-9. ]+)\)',samplesLine) # skip count value
    samples=[float(x) for x in samplesString.split()]
    if len(samples)!=samplesCount:
        raise Exception("Samples count mismatch: expected: %d, actual: %d" % (samplesCount,len(samples)))
    return Channel(channelName,samples)
   
def readSetting(settingsFile):
    try:
        pdb.gimp_message("readSetting try")
        nameLine,timeLine,linearLine=islice(settingsFile,3)
    except ValueError:
        pdb.gimp_message("readSetting Valueerror")
        return None
    pdb.gimp_message("readSetting settingNameTime")   
    pdb.gimp_message(nameLine)   
    pdb.gimp_message(timeLine)   
    settingName=extract(r'\(GimpCurvesConfig "(.+)"',nameLine)
    settingTime=int(extract(r'\(time (\d+)\)',timeLine))
    pdb.gimp_message("readSetting after settingNameTime")
    channels=[]
    for channelName in ["value","red","green","blue","alpha"]:
        pdb.gimp_message("channelName in list")
        channel=readChannel(settingsFile,channelName)
        if channel.samples:
            channels.append(channel) # Don't add identity channels
    return Setting(settingName,settingTime,channels)

def settingsFrom(settingsFile):
    # Skip top two lines
    pdb.gimp_message("inside settingsFrom")
    _,_=islice(settingsFile,2)
    while True:
        pdb.gimp_message("settingsFrom while Loop")
        setting=readSetting(settingsFile)
        pdb.gimp_message("settings after readSetting")
        if not setting:
            break
        yield setting

def load(settingsFilePath):
    settings=[]
    pdb.gimp_message("inside load")
    with codecs.open(settingsFilePath, 'r', encoding='utf-8') as settingsFile:
        pdb.gimp_message("before settings From")
        for setting in settingsFrom(settingsFile):
            pdb.gimp_message("for loop inside Settings from")
            if setting.time==0: # Only named settings
                pdb.gimp_message("setting.time==0")
                settings.append(setting)
    return settings

# if __name__=="__main__":
#     settings=load("./GimpCurvesConfig.settings")
#     for s in settings:
#         print "### %s" % s.name
#         for c in s.channels:
#             print "   %s: %d" % (c.name,len(c.samples))
#             for sample in c.samples[:5]:
#                 print "        %7.5f" % sample

def python_cc_test(image,layer):
    pdb.gimp_image_undo_group_start(image)
    # pdb.gimp_context_push()
    #YOUR CODE BEGINS=======================
    pdb.gimp_message("what")
    settings=load("C:\\Users\\tintran\\AppData\\Roaming\\GIMP\\2.10\\curves\\export1")

    # for s in settings:
    #     pdb.gimp_message("### " + str(s.name))
    #     for c in s.channels:
    #         pdb.gimp_message("   "+c.name+":"+" "+str(len(c.samples)))
    #         for sample in c.samples[:5]:
    #             pdb.gimp_message("        "+str(sample))

    #pdb.gimp_message(get4Points(ovectors))
    #YOUR CODE ENDS ========================
    # pdb.gimp_context_pop()
    pdb.gimp_image_undo_group_end(image)
    pdb.gimp_displays_flush()

register(
    "python_fu_cc_test",
    "Color Curves Test",
    "Color Curves Test",
    "TT",
    "TT",
    "2024.1.25",
    "<Image>/Python-Fu/A CC Test...",
    "*",      # Create a new image, don't work on an existing one
    [
    #INPUT BEGINS
    #INPUT ENDS
    ],
    [],
    python_cc_test)

main()

_________________
TinT


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 8:06 am  (#9) 
Offline
GimpChat Member
User avatar

Joined: Jan 20, 2013
Posts: 14816
Location: roma, italy
Well, Tin, I don't need to "export" a preset but to "load" an existing one (already stored in the appropriate folder) and apply it to the layer active in the python filter.

_________________
"Where am I ?"


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 8:11 am  (#10) 
Offline
GimpChat Member
User avatar

Joined: Jan 20, 2013
Posts: 14816
Location: roma, italy
Any how my first impression from all your lines of code: I'm feeling really a "caveman" as far as a gimp python filter structure is concerned. My filters are all so "troglodyte"...
Hope at end to be able to add -with all your help- the possibility for the user of my filter to:
- select a preset (from those already stored and accessible) and apply it to modify the colors look of the layer.
Thanks

_________________
"Where am I ?"


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 8:22 am  (#11) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 4000
Location: Canada
Yeah but it needs to load from a file. And the file with multiple named color curves is my first step in testing and I can't even do that.
I feel like a caveman too. I just cut and paste many things.

_________________
TinT


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 8:28 am  (#12) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 4000
Location: Canada
Actually do you have file with named settings I can use...my GIMP isn't exporting them.

_________________
TinT


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 8:42 am  (#13) 
Offline
GimpChat Member
User avatar

Joined: Jan 20, 2013
Posts: 14816
Location: roma, italy
This is what I put (copied from some post on GC) on samj's folder:"...Preferences/Filters"

Attachment:
GimpCurvesConfig.7z [158.5 KiB]
Downloaded 27 times

_________________
"Where am I ?"


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 12:09 pm  (#14) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 4000
Location: Canada
Hi Diego,
With your sample files
I was able to use ofnuts's code with minor changes here
#!/usr/bin/env python
from gimpfu import *
import os,sys,traceback,re,codecs

from itertools import islice

class Channel:
    def __init__(self,name,samples):
        self.name=str(name)  # Never unicode
        self.samples=samples
   
class Setting:
    def __init__(self,name,time,channels):
        self.name=name
        self.time=time
        self.channels=channels

def extract(pattern,line):
    matched=re.match(pattern,line.strip())
    return matched.group(1) if matched else None

def readChannel(settingsFile,channelName):
    nameLine,_,typeLine,pointsLine,pointsTypeLine,samplesCountLine,samplesLine=islice(settingsFile,7)
    #pdb.gimp_message("nameline" +  nameLine)
    #pdb.gimp_message("typeLine" + typeLine)
    #pdb.gimp_message("pointsLine" + pointsLine)
    #pdb.gimp_message(pointsTypeLine)
    #pdb.gimp_message(samplesCountLine)
    #pdb.gimp_message(samplesLine)
    actualName=extract(r'\(channel (\w+)\)',nameLine)
    #pdb.gimp_message(actualName)
    if actualName!=channelName:
        #pdb.gimp_message("1")
        pdb.gimp_message("Channel name mismatch: expected: "+channelName+", actual: "+actualName)
       
    if pointsLine.strip()=='(points 4 0 0 1 1)': # identity for channel
        #pdb.gimp_message("2")
        return Channel(channelName,None)
    #pdb.gimp_message("3")   
    samplesCount=int(extract(r'\(n-samples (\d+)\)',samplesCountLine))
    samplesString=extract(r'\(samples \d+ ([0-9. ]+)\)',samplesLine) # skip count value
    samples=[float(x) for x in samplesString.split()]
    if len(samples)!=samplesCount:
        raise Exception("Samples count mismatch: expected: %d, actual: %d" % (samplesCount,len(samples)))
    return Channel(channelName,samples)
   
def readSetting(settingsFile):
    try:
        #pdb.gimp_message("readSetting try")
        # nameLine,timeLine,linearLine=islice(settingsFile,3)
        nameLine,timeLine=islice(settingsFile,2)
        #pdb.gimp_message("nameLine" + nameLine)
        #pdb.gimp_message("timeLine" + timeLine)
        ##pdb.gimp_message("linearLine" + linearLine)
    except:
        #pdb.gimp_message("readSetting Valueerror")
        return None
    #pdb.gimp_message("readSetting settingNameTime")   
    #pdb.gimp_message(nameLine)   
    #pdb.gimp_message(timeLine)   
    settingName=extract(r'\(GimpCurvesConfig "(.+)"',nameLine)
    settingTime=int(extract(r'\(time (\d+)\)',timeLine))
    #pdb.gimp_message("readSetting after settingNameTime")
    channels=[]
    for channelName in ["value","red","green","blue","alpha"]:
        #pdb.gimp_message("channelName in list")
        channel=readChannel(settingsFile,channelName)
        if channel.samples:
            channels.append(channel) # Don't add identity channels
        #pdb.gimp_message("done")   
    return Setting(settingName,settingTime,channels)

def settingsFrom(settingsFile):
    # Skip top two lines
    #pdb.gimp_message("inside settingsFrom")
    _,_=islice(settingsFile,2)
    while True:
        try:       
            setting=readSetting(settingsFile)
        except:
            setting = None
        #pdb.gimp_message("settings after readSetting")
        if not setting:
            break
        yield setting

def load(settingsFilePath):
    settings=[]
    #pdb.gimp_message("inside load")
    with codecs.open(settingsFilePath, 'r', encoding='utf-8') as settingsFile:
        #pdb.gimp_message("before settings From")
        for setting in settingsFrom(settingsFile):
            #pdb.gimp_message("for loop inside Settings from")
            if setting.time==0: # Only named settings
                #pdb.gimp_message("setting.time==0")
                settings.append(setting)
    return settings

# if __name__=="__main__":
#     settings=load("./GimpCurvesConfig.settings")
#     for s in settings:
#         print "### %s" % s.name
#         for c in s.channels:
#             print "   %s: %d" % (c.name,len(c.samples))
#             for sample in c.samples[:5]:
#                 print "        %7.5f" % sample

def python_cc_test(image,layer):
    pdb.gimp_image_undo_group_start(image)
    # pdb.gimp_context_push()
    #YOUR CODE BEGINS=======================
   
    #CHANGE BELOW to POINT TO YOUR OWN Color Curves Settings
    settings=load("C:\\Users\\tintran\\AppData\\Roaming\\GIMP\\2.10\\curves\\GimpCurvesConfig.settings")
    pdb.gimp_message(settings[0].name)
    pdb.gimp_message(settings[0].time)
    pdb.gimp_message(settings[0].channels[0].name) #value channel
    pdb.gimp_message(settings[0].channels[1].name) #red channel
    pdb.gimp_message(settings[0].channels[1].samples) #red channel
    redchannel_samples = settings[0].channels[1].samples
    pdb.gimp_drawable_curves_explicit(layer,HISTOGRAM_RED,len(redchannel_samples),redchannel_samples)

    # for s in settings:
    #     #pdb.gimp_message("### " + str(s.name))
    #     for c in s.channels:
    #         #pdb.gimp_message("   "+c.name+":"+" "+str(len(c.samples)))
    #         for sample in c.samples[:5]:
    #             #pdb.gimp_message("        "+str(sample))

    ##pdb.gimp_message(get4Points(ovectors))
    #YOUR CODE ENDS ========================
    # pdb.gimp_context_pop()
    pdb.gimp_image_undo_group_end(image)
    pdb.gimp_displays_flush()

register(
    "python_fu_cc_test",
    "Color Curves Test",
    "Color Curves Test",
    "TT",
    "TT",
    "2024.1.25",
    "<Image>/Python-Fu/A CC Test...",
    "*",      # Create a new image, don't work on an existing one
    [
    #INPUT BEGINS
    #INPUT ENDS
    ],
    [],
    python_cc_test)

main()

_________________
TinT


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 12:31 pm  (#15) 
Offline
Script Coder
User avatar

Joined: Oct 25, 2010
Posts: 4752
    #CHANGE BELOW to POINT TO YOUR OWN Color Curves Settings
    settings=load("C:\\Users\\tintran\\AppData\\Roaming\\GIMP\\2.10\\curves\\GimpCurvesConfig.settings")

No need to hardcode file paths. Normally this should be:
settings=load(os.path.join(gimp.directory,'filters','GimpCurvesConfig.settings'))

_________________
Image


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 3:26 pm  (#16) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 4000
Location: Canada
thanks for that ofnuts :D

_________________
TinT


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 9:14 pm  (#17) 
Offline
GimpChat Member
User avatar

Joined: Jan 20, 2013
Posts: 14816
Location: roma, italy
Sorry Tin and Ofnuts.
- if I copy what you posted (included ofnuts' suggested change) I get a list of messages, but nothing else
- whatever I add in the code, for instance create a new layer, it is executed but then no more filter messages but an error
Attachment:
error-msg.PNG
error-msg.PNG [ 31.22 KiB | Viewed 584 times ]


As long as my little brain does not see at least one curve applied to an existing image I can't try to use the proposed flow.
You too Tin says: does not work on existing image, which is what I need: apply a curve to the input layer.
Thanks a lot for trying.

_________________
"Where am I ?"


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 9:51 pm  (#18) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 4000
Location: Canada
can you share you code?
it looks like it's not reading the file.
Update:
I think instead of
settings=load("C:\\Users\\tintran\\AppData\\Roaming\\GIMP\\2.10\\curves\\GimpCurvesConfig.settings")

with ofnuts piece it's should be.
settings=load(os.path.join(gimp.directory,'curves','GimpCurvesConfig.settings'))

_________________
TinT


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Fri Jan 26, 2024 11:31 pm  (#19) 
Offline
GimpChat Member
User avatar

Joined: Dec 09, 2018
Posts: 656
I'm on an old version of 2.10 and my GimpCurvesConfig.settings has lines:
(linear no)
which Ofnuts script is also expecting.
However Tim has taken it out as the, presumably later version, file on post #13 doesn't have them.

Also I'm not getting this even though I deliberately saved a preset that does nothing:

    if pointsLine.strip()=='(points 4 0 0 1 1)': # identity for channel
            return Channel(channelName,None)

If I print pointsLine I get loads of these:
(n-points 17)

Whereas the above check looks like it's better aimed at pointsTypeLine. This is an example print of one of pointsTypeLine:

(points 34 0.000000 0.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 1.000000 1.000000)

Was there another format change to get rid of the -1s in between the 0 0 and 1 1?


Top
 Post subject: Re: use of a color curves preset in a python filter
PostPosted: Sat Jan 27, 2024 12:13 am  (#20) 
Offline
GimpChat Member
User avatar

Joined: Jan 20, 2013
Posts: 14816
Location: roma, italy
trandoductin wrote:
can you share you code?
it looks like it's not reading the file.
Update:
I think instead of
settings=load("C:\\Users\\tintran\\AppData\\Roaming\\GIMP\\2.10\\curves\\GimpCurvesConfig.settings")

with ofnuts piece it's should be.
settings=load(os.path.join(gimp.directory,'curves','GimpCurvesConfig.settings'))

this is my filter as tested with my additions
Attachment:
python_cc_test.7z [2.04 KiB]
Downloaded 17 times

note: I left ofnuts indication (filters, not curves), it doesn't find "curves"

_________________
"Where am I ?"


Top
Post new topic Reply to topic  [ 43 posts ]  Go to page 1, 2, 3  Next

All times are UTC - 5 hours [ DST ]


   Similar Topics   Replies 
No new posts Attachment(s) How to call a python filter to run interactively

10

No new posts Attachment(s) Python calling G'MIC filter? (Solved)

4

No new posts New GEGL filter that inverts transparency and allows a color fill

2

No new posts Attachment(s) New G'MIC filter "Color Wheel"

12

No new posts Attachment(s) Like GIMP's new 3D transform preset.

5



* Login  



Powered by phpBB3 © phpBB Group