Merge lp:~jochym/phatch/Actions into lp:phatch

Proposed by Paweł T. Jochym
Status: Merged
Merged at revision: not available
Proposed branch: lp:~jochym/phatch/Actions
Merge into: lp:phatch
Diff against target: 415 lines (+406/-0)
2 files modified
phatch/actions/grid.py (+232/-0)
phatch/actions/warm_up.py (+174/-0)
To merge this branch: bzr merge lp:~jochym/phatch/Actions
Reviewer Review Type Date Requested Status
Stani Pending
Review via email: mp+19100@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Paweł T. Jochym (jochym) wrote :

I have developed two fairly usefull actions:
- making grid of images (grid) and
- toning (warm_up).
They prove useful in the working photographer toolbox.
They are based on other actions from the repo and
are appropriatly licensed (GPL3).
I'll be glad if this code can be included in the phatch distribution.
I am new to the phatch development but tried to obey the rules.

Revision history for this message
Stani (stani) wrote :

Hi Pawel,

Thanks a lot! Unfortunately I am very busy ATM for preparing a pycon talk:
http://us.pycon.org/2010/conference/schedule/event/38/

So I will be only able to review it properly in begin march. Feel free to continue to make actions, the best is one branch for each action.

I have added you to the phatch-dev team:
https://launchpad.net/~phatch-dev

Please email the mailing list with a small introduction of yourself.

Thanks in advance!

Revision history for this message
Stani (stani) wrote :

Hi Pawel,

Due to Ubuntu Feature Freeze, I'll try to get in quicker.

The WarmUp action does not respect the transparency, at least not of a gif image I tried. Could you build in support for transparency? (Test with RGBA and GIF.) I know these are not common cases but Phatch should work always.

Please make the label 'Warm Up' instead of 'WarmUp'. Use the following tags for now:
- warm up: filter
- grid: size, filter

Have a nice day!

Revision history for this message
Paweł T. Jochym (jochym) wrote :

stani pisze:
> Hi Pawel,
>
> Due to Ubuntu Feature Freeze, I'll try to get in quicker.
>
> The WarmUp action does not respect the transparency, at least not of a gif image I tried. Could you build in support for transparency? (Test with RGBA and GIF.) I know these are not common cases but Phatch should work always.
>
> Please make the label 'Warm Up' instead of 'WarmUp'. Use the following tags for now:
> - warm up: filter
> - grid: size, filter
>

Great. Expect correction tomorrow.

Paweł
--
Paweł T. Jochym
Institute of Nuclear Physics, PAN
Cracow, Poland

lp:~jochym/phatch/Actions updated
1551. By Paweł T. Jochym

Handle gif transparency semi-inteligently in warmup. Correct tags. Performance improvements in warmup.

Revision history for this message
Paweł T. Jochym (jochym) wrote :

> Hi Pawel,
>
> Due to Ubuntu Feature Freeze, I'll try to get in quicker.
>
> The WarmUp action does not respect the transparency, at least not of a gif
> image I tried. Could you build in support for transparency? (Test with RGBA
> and GIF.) I know these are not common cases but Phatch should work always.
>

I have implemented it. It works but I do not like my implementation. Unfortunately convert('P') does not handle transparency properly or I dont know how to use it.
For now I've pushed it to the save function - so the action returns RGB/RGBA and the save makes the conversion. It works but there is no control over palette type and there is ugly warning about conversion in the log. On the other hand the action changes the palette completely so one should convert it properly in the script anyway.
I have improved the speed slightly also.

> Please make the label 'Warm Up' instead of 'WarmUp'. Use the following tags
> for now:
> - warm up: filter
> - grid: size, filter
>

Done

Revision history for this message
Stani (stani) wrote :

> I have implemented it. It works but I do not like my implementation.
> Unfortunately convert('P') does not handle transparency properly or I dont
> know how to use it.
Sorry, I forgot to say that we've solved most PIL issues in phatch/lib/imtools.py Please use as much code from that as possible. So to convert the mode use:
imtools.convert(image, 'L')
and for transparent images:
imtools.convert(image, 'LA')
http://bazaar.launchpad.net/~stani/phatch/trunk/annotate/head%3A/phatch/lib/imtools.py#L580

So never use PILs image.convert method directly. We have a whole test suite with all kinds of images and image.convert is not bullet proof. After Pycon we will fire your actions in the test suite to see if any issue arises.

If the imtools.convert function would not behave properly, than we will fix it. It is important to have all the convert fixes centrally in one function.

> For now I've pushed it to the save function - so the action returns RGB/RGBA
> and the save makes the conversion. It works but there is no control over
> palette type
Our rule is that if an action needs another color mode, that we don't convert it back. The amount of conversions should be limited to the minimum. So you did the right thing by pushing the conversion to save action. Note that the user in this case could add an extra convert action, although I think we don't support yet there palette suboptions. Probably we might in the future.

> and there is ugly warning about conversion in the log.
The warning is not meant to be ugly, but informative. It mostly should warn the user. Maybe based on this warning he decides to save it as png instead of gif. We chose to inform the user always when a conversion is needed to save in a certain file type.

> On the
> other hand the action changes the palette completely so one should convert it
> properly in the script anyway.
Let's see how imtools.convert handles this. The use of this action for gif P images is probably a corner case, but the most important is that transparency doesn't get lost.

> I have improved the speed slightly also.
>
> > Please make the label 'Warm Up' instead of 'WarmUp'. Use the following tags
> > for now:
> > - warm up: filter
> > - grid: size, filter
> Done

Great! Could you please use other icons. If you are lazy I prefer you copy the icons from these actions:
- grid: tile/mirror
- warm up: brightness

If you have a bit more time, look for a nice GPL icon. We prefer shiny icons to make them coherent. We only use SVG icons, as you can find in the source tarball. With Inkscape you export to a png of 48x48. You can use img2py to convert the png to py code.

Examples of icons can be found in the source:
images/source/igor
images/source/openclipart

Keep up the good work!

Revision history for this message
Stani (stani) wrote :

Can you also send me an image privately which is good as a demo for the warm up action? Also you'll need to document your actions. I'll send you a rst template later.

Revision history for this message
Paweł T. Jochym (jochym) wrote :

This branch should not be considered for merging any more.
It has been forked into separate action branches (warm-up and grid).
I'm marking it as abandoned.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'phatch/actions/grid.py'
2--- phatch/actions/grid.py 1970-01-01 00:00:00 +0000
3+++ phatch/actions/grid.py 2010-02-12 10:56:17 +0000
4@@ -0,0 +1,232 @@
5+# Phatch - Photo Batch Processor
6+# Copyright (C) 2007-2008 www.stani.be
7+#
8+# This program is free software: you can redistribute it and/or modify
9+# it under the terms of the GNU General Public License as published by
10+# the Free Software Foundation, either version 3 of the License, or
11+# (at your option) any later version.
12+#
13+# This program is distributed in the hope that it will be useful,
14+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+# GNU General Public License for more details.
17+#
18+# You should have received a copy of the GNU General Public License
19+# along with this program. If not, see http://www.gnu.org/licenses/
20+
21+# Embedded icon is designed by Igor Kekeljevic (http://www.admiror-ns.co.yu).
22+# Based on standard Stani's actions
23+# This action is (C) 2010 by Pawel T. Jochym <jochym@gmail.com>
24+
25+# Make m x n grid with copies of imput image
26+
27+from core import models
28+from core.translation import _t
29+from lib.imtools import has_transparency
30+from math import sqrt
31+
32+#---PIL
33+def init():
34+ global Image
35+ import Image
36+ global HTMLColorToRGBA
37+ from lib.colors import HTMLColorToRGBA
38+
39+def make_grid(image,grid,background_colour,old_size=None,scale=True):
40+ #check if there is any work to do
41+ if grid[0]==1 and grid[1]==1:
42+ return image
43+ #because of layer support photo size can be different from image layer size
44+ if old_size is None:
45+ old_size = image.size
46+ if scale :
47+ # Keep the same number of pixels in the result
48+ s=sqrt(grid[0]*grid[1])
49+ old_size = tuple(map(lambda x: int(x/s) , old_size))
50+ image=image.resize( old_size, getattr(Image,'ANTIALIAS'))
51+
52+ new_size=grid[0]*old_size[0],grid[1]*old_size[1]
53+
54+ #displacement
55+ dx, dy = old_size
56+ n, m = grid
57+
58+ #mode
59+ if has_transparency(image) and image.mode != 'RGBA':
60+ # Make sure 'LA' and 'P' with trasparency are handled
61+ image = image.convert('RGBA')
62+ else :
63+ image = image.convert('RGB')
64+
65+ new_canvas = Image.new(image.mode,new_size,background_colour)
66+
67+ for x in range(n):
68+ for y in range(m):
69+ new_canvas.paste(image,(x*dx,y*dy))
70+
71+
72+ return new_canvas
73+
74+
75+#---Phatch
76+class Action(models.Action):
77+ label = _t('Grid')
78+ all_layers = True
79+ author = 'Pawel T. Jochym'
80+ email = 'jochym@gmail.com'
81+ init = staticmethod(init)
82+ pil = staticmethod(make_grid)
83+ version = '0.2'
84+ tags = [_t('size'),_t('filter')]
85+ update_size = True
86+ __doc__ = _t('Make n x m grid of image')
87+
88+ def interface(self,fields):
89+ fields[_t('Width')] = self.SliderField(1,1,8)
90+ fields[_t('Height')] = self.SliderField(1,1,8)
91+ fields[_t('Background Color')] = self.ColorField('#FFFFFF')
92+ fields[_t('Scale to keep size')] = self.BooleanField(True)
93+
94+ def values(self,info):
95+ #size
96+ x0, y0 = info['size']
97+ x1 = self.get_field('Width',info)
98+ y1 = self.get_field('Height',info)
99+ grid = x1,y1
100+ #parameters
101+ return {
102+ 'old_size' : (x0,y0),
103+ 'grid' : grid,
104+ 'background_colour' : self.get_field('Background Color', info),
105+ 'scale' : self.get_field('Scale to keep size', info),
106+ }
107+
108+ icon = \
109+'x\xda\x01\xde\x0c!\xf3\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x000\x00\
110+\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\x00\x00\x00\x04sBIT\x08\x08\x08\
111+\x08|\x08d\x88\x00\x00\x0c\x95IDATh\x81\xd5\x98{l\x15\xd7\x9d\xc7\xbf\xe71\
112+\xcf{\xaf_8\x18\x12\x1e1\x84\x90\xa6\xa0nK\x9bGE\x9b*m\xa1EU[5\x9b\x92\x18\
113+\xb1\t\xc4\x0b!B%\xa0d\x1b\xb6dS\x08\xf2J(U\xb4E\x11\x8eS7\xe6aSbJ\xbdlD\x10\
114+\xc2\xc4)\xb4\r\x84\x10\xd7\xc1IA8\x0e\x14\xfb:\xd8\x80\x1f\xb1\xef\xcc\x9ds\
115+\xe6\xec\x1fw\xc6\x8c/\xd7\t\x8f\x95\xca\x8e\xf4\xd3\xd8s\xee\xcc\xf9~~\xbf\
116+\xef\xef\x9c\xb9\x97(\xa5\xf0\xff\xf9\xa0\xffh\x01\xd7{\xf0\x7f\xb4\x80+9\
117+\xaa\xab\xab\xbf$\x84\xf8\xaa\xe7y{\x96/_\x9e\x8c\x8e\x91\x1b\xd9B[\xb6l\x99\
118+\xef\xfb\xfe\x7f\xf9\xbe_"\x84\x08/W.Y\xb2dY\xf8\xcf\r\x0b\xb0m\xdb\xb6\xdf1\
119+\xc6\xe6{\x9e\x07\xcf\xf3 \xa5\x1c\x1e\x93R\x16-[\xb6\xec"p\x83\xf6@}}}\xa3m\
120+\xdb\xf3\x01H!\xc4s\x8c\xb1q\x84\x90\xd7\xc2qM\xd3\x1e\x08\xff\xbe\xe1z\xa0\
121+\xa1\xa1\xe1\xd9x<~\x7f___w*\x95\x9a\xbdx\xf1\xe2\x93\xc1\xd0\xfc\xaa\xaa\
122+\xaa\x7f\x06@\xa5\x94}\xe1\xe7o\xa8\n\xec\xdb\xb7obaa\xe1/]\xd7UR\xca/G\xc4\
123+\x03\x00\x18c.\x00\xd8\xb6\xfdVx\xed\x86\x02\xe0\x9c\xbf"\x84\xa0\x8e\xe34-X\
124+\xb0\xa0#:\xb6m\xdb\xb62B\x88\x05\xe0\xe3\x85\x0b\x17\x9e\x0b\xaf\xdfP\x00\
125+\xb6m\x7f\xc3q\x1c\xc5\x18+\xcb1\xfc\xa2\x10\x02J\xa9\r\xd1\x8b7\x12\x00\xa1\
126+\x94\xdaB\x08\xf5\xe0\x83\x0fvE\x07v\xec\xd8Q-\xa5\x1c\x0b\xe0\xafK\x97.\xdd\
127+\x14\x1d\xfb?\x01X\xbbv-\xdf\xbcy\xf3\xa6\xed\xdb\xb7\xdf{\xad\xcfhjj\x8a\t!\
128+ \xa5\xa4555K\x00`\xfb\xf6\xed\xb7\xee\xdc\xb9\xf3\x8c\xe38\x8b]\xd7\x1db\
129+\x8c\xdd\x9f}\xdfu\xef\x03[\xb7n-\xf5<\xef\x00\xa5\xf4VJ\xa9\xcf\x18[\xbb`\
130+\xc1\x82u\xa7N\x9d\xbay```\x8d\xef\xfb\xcc\xf7\xfdNJ\xe9\xa9X,v\xf2\x8e;\xee\
131+8\x01`\x00\xc0\x88\x89\xdfx\xe3\x8d[m\xdbnO&\x93H\xa5R\xb0,kH)e\x0e\x0e\x0eR\
132+\xdf\xf7{)\xa53\xca\xcb\xcb;\xb2\xe7\xbf.\x80\x9a\x9a\x9a\xb2t:\xfd*\xa5To\
133+\xf3\xdf\xc1D\xfd\x0b(-\x99\x8e[n\xb9\xc5K\xa7\xd3\xbc\xad\xad\x8d\x0c\x0e\
134+\x0eB\xd34$\x12\t\xc4\xe3q\xd8\xb6\r\xc30\x14\xe7\xdcWJ\xc9T*\x95J&\x93\xe5\
135+\xb1X\xec\x02!\xa4\xb1\xab\xab\x0b\x8e\xe3\x84S(BH\x93\xa6i?y\xf4\xd1G{si\
136+\xb8\xa6}`\xed\xda\xb5|\xdc\xb8q5R\xca\x05\x8aI\x1cr\xb7\xe3\xa4\xfa\x0b\xfe\
137+\xa5\xf4\x17(\x88\xdf\x8d\xc3\x87\x0fk\xd1\xc4p\xce\x91N\xa7\x91J\xa5\x00\
138+\x00\xbe\xef\x13]\xd7\x99\x10\x829\x8e\xa3\x13B6\x12B\xde\xe9\xed\xed\x85\
139+\xeb\xba\x00 \x01\xec\x97R.[\xb6lY\xfbgi\xb9\xea\n\xbc\xf4\xd2K\x93}\xdf\x7f\
140+\x13@\xe9\xa7\xe4<\x1a\xc5op\x81t`\xc1\x1d\xff\x86/\xf1\xef\xe0\xa3\xb6\x91\
141+\xf3q\xceaY\x16L\xd3\x84eY0\x0c\x03\xba\xaeC)\x05\xcf\xf3\xe08\x0eR\xa9\x94\
142+\xd24\xad\xff\xdc\xb9s\xf9RJ\x07\xc0W\x97,Y\xd2z%z\xae\xaa\x02\x95\x95\x95\
143+\x0f9\x8e\xb3\x991\xa6\x9fE+\xfe(k\xe1\xd1\x14\xcan\x7f\x1a\xd3\xbd\xd9\xf8\
144+\xe8\xf4\xe5\xc9\xe2\x9c\x0f\x07\xa5\x14\x84\x10\x04\xcd\n\xd7u\xc3 \xfd\xfd\
145+\xfd\xf9RJPJ\xbfV^^\x9eK<AV\xdf\\1@}}=K&\x93\xaf\xa6\xd3\xe9\x85\x84\x11\x1c\
146+\x95\xaf\xe3\xb8\xdf\x08\xc2\x80\xfbo\xfe)\xbeH\xbe\x85\xb3\x9dgs\x8a\xd74\r\
147+\x8c1P\x9aY\xf0\x82\xb5\x1c\x9e\xe7\r\xdb\xaa\xa7\xa7\x07\x03\x03\x03(..~\
148+\xaf\xbc\xbc\xfcxx\xff\xc6\x8d\x1b\xf3\x0c\xc3\xf89\x80\x87\x01L\x00\xf0\x8e\
149+\xef\xfb+\x1f\x7f\xfc\xf1#\xe1g>w\x19\xad\xad\xad\x9d\xd0\xd9\xd9yB\x08\xb1\
150+\xd0c)\xec\x13\x95\xc3\xe2\'\xc5\xa7\xe3[\xf9e8\xfb\xf7\xcb\xc5\x87\x00Q\xf1\
151+\xbe\xefC\x081\x9cy\xc7qp\xfe\xfcy\xb4\xb4\xb4`\xf6\xec\xd9p\x1c\xe7\xd3\xe0\
152+V\xf2\xf2\xcb/?f\x18F\x1b\x80\x7f\x07P\n@\x03\xf0uJ\xe9\xe6\xaa\xaa*-\x9c\
153+\xe33{\xa0\xba\xba\xfa\xc7CCC\xdb\x95R\xe6\'\xea#\xbc\x99~\x15C\xb4\x1f\x84\
154+\x02\x84\x02\x8c2\x18\xcc\x86F\r\xe80\xc1\x1c\x13\xb2\xc7\x04\x1b4\xa1\x13\
155+\x0bq3\x0fq+\x0f\t+\x0fE\xe6x\x8c\xd1\xc7\xc30\x0ch\x9a\xa6\x00\x10\xc7q\xf0\
156+\xee\xbb\xefb\xe6\xcc\x99\x981c\x06ZZZ\xc0\x18\xfb-\xa5t\x86R\xea.\x00\x17\
157+\x01\xd4\xfb\xbe\xbf\x83\x10r/!d=\x00PJ\xef,//\xff\x10\x18\xddB\xe4\x95W^yY\
158+\x08Q\xce\x18#\xef\x8b\x038,\xff\x00\xc5}P\x92\xa9\x1b\xa1\x80\xa2\x12.\x1d\
159+\x80K\x07 ]\xc0\xf9\x04Hu\x00\xd2\xcd\x18\x16\x0c\xa0\x1a@u\x80\x9a\xc0\xd8\
160+\xc4\xcd\x98wS9JK\xa6\x931c\xc6$\x0f\x1f><p\xdbm\xb7\xdd\xde\xde\xde\x8et:\
161+\x1dVmq\x90\xd43\x8e\xe3\xdc\xb5b\xc5\x8a\xee@SSeee\x99R\xea\xcet:\xad\x13B\
162+\x88RJ]\x06P__?\xae\xaf\xaf\xef\x80R\xea\x0b>\xf5p ]\x83v\xf5^F\x0c\x05@2\
163+\xe2C\x08B\x01_\x00^/\xe0\xf6\x00J\x02\xcc\xcc\x88f&\xc0\xf3\x00=\x1f\xd0\n\
164+\x01?\xd6\x89&\xebE\x8c\xcb\xff\x0f\x14\xc4\xa6\x8e\x9f;w\xee\xf8c\xc7\x8e\
165+\xa1\xa8\xa8\x08\xba\xaeKB\x08SJA)\x95r\x1c\xe7\x81\x95+W\xf6df\xca\xe4C\x08\
166+1N)\xe5uuuu\x02 \x84\x90\x91\x16\xaa\xad\xad\xfd~:\x9d\xde\xa9\x94\xb2\xcf\
167+\xfb\x1d\xd8\xeb\xbe\x84^\xf5IF4\t\x1eC.\t\x0f\xaf\x8b\x01`\xa83s\xa6\x06\
168+\xa0\xe5\x07\x91\x00\x98}\t\x86r\x80p\x80P\x8a9V9\xbe\xa3\xff+\x08\x08\xce\
169+\x9d;\xe7\x9e>}z\x83\x94\xf2Y\xa5\x14\xa4\x94\xab\x9e|\xf2\xc9\x8d\x11\xf1\
170+\xe4\x89\'\x9e(\x996m\xda\xc7J\xa9\xa6U\xabV\xcd\x01\xe0\x03\xf0\xc3\n\x90\
171+\xba\xba\xba\x17\x95R?\xe3\x9c\x93\xbfy\x7fF\xa3\xf8-\x04Kg\xee\x8e\x8a\x0f-\
172+\x14\x9c\xa12V1o\xa2\xd0\'q\x18q\x0e\xcdd\xd0\x0c\x0e\xa2\x07\xa2\x03\xbb)"!\
173+\x88\x80O<\xecs\xabpF\x1e\xc7\xc3\xe6z\x8c\x1d;\xd6\xc8\xcf\xcf\x7f\xec\xfd\
174+\xf7\xdfG*\x95\x82\xe38\x07\x03{\xd3\x10b\xd2\xa4I+\xa5\x94p\x1cgsdf\xc2\x01\
175+`\xeb\xd6\xad\x13\xa5\x94\x8f\x1b\x86A|\xe5#)O\xc0\xe7\xe9KKTT|\x14\x82\x02\
176+\x9ci\x88\x99&t\xaeC\xd7\r\xe8\xba\x96Y}(\x03a4c;\n("\xa1\xa8\x84P\x02B\xa5!\
177+T\x1a\x17U\'.\xaaN\xd8$\x0f\x8c\xb1\x9b9\xe7\xf0<O\xee\xd9\xb3\xa7-\x00`\x00\
178+\xd8\xea\xd5\xab\x7f\n`\x85\xe7yMk\xd6\xac\xf9}\x04@\r[\xa8\xa2\xa2\xe2\x9f\
179+\x8a\x8b\x8b\x1b\x0b\n\n\x8a\x00\xe0\xb8h\xc2\xfe\xf4o \xe1\xe5\xb4\x10%\x04\
180+\x86n\xc3\xd2l\xe8\xdc\x84\xc9M\xe8\xdc\x00g:4\xae\x83S\x06\xca\x18(%\x00QPT\
181+\xc1W\x02By\x10\xca\xc3xr;~\xa0\xaf\x80\xa9\x12\xe8\xed\xedEsss\xf5\xd0\xd0\
182+\xd0c\x00D{{\xfb\xac\xaa\xaa\xaa\xb3\x13\'N\xb4\xe6\xcf\x9f\xff\xa3\x82\x82\
183+\x82_\x11B>mmm\x9d[WW\xd7\x06@\x04!G\xf4\xc0\x86\r\x1b\x12\x96e\xfdOII\xc9}\
184+\x9a\xa6\xe1\x13\xbf\x1d\xff\xedn\x18\xee\x83h\xf6m-\x06S\x8b\xc1\xd2b\xb0\
185+\xb8\rC\xb3`p\x0b:7\xa01\x03\x1a\xe3`4\xb3\xfb\x82\x00\n\x19\x00\xa9\x04\xee\
186+d\xf7a\x16\xfb\x01\xa0\x80\xd3\xa7O{\x87\x0e\x1dzd\xfd\xfa\xf5\xbb\x9f\x7f\
187+\xfe\xf9n\xa5\x94\xa5\x94\xea\x12B\xbc\xc39\xff&!$_)\xd5}\xe2\xc4\x89\x07\
188+\xeb\xea\xea>\x00\xe0\x05!\x00\x88\x9c\xfb\xc0\x0b/\xbc\xb0z\xc2\x84\t\xebc\
189+\xb1\x18u1\x88\xdd\xe9\x17\xd1&\x8f\x0cC\x18\xdc\x84\xa5\xc5ak1XZ\x1cV\x00c2\
190+\x1b\xa6fAg&tf\x80S\x0eJ9\x08\x08\x14\x14\x188\xbe\xc2\xe6a<\x99\x06!\x04\
191+\x8e\x1d;\xd6\xfd\xdak\xaf}w\xd7\xae]\x7f\x07\x80g\x9eyf\x8da\x18+\xa3Z\x84\
192+\x10\x07[[[\xd7444\x9c\x0cDG\x01\xe4\xa8\x1b\xd9s\xcf=wwii\xe9\xde\x92\x92\
193+\x92\x02B\t\xfe$v\xa0)\xbd\x05 >,=\x8e\x98\x96\x80\xcd\x13\xb0\xf5x\x06\x86\
194+\'`\xf1\x18L\x1e\x83\xc9m\x18\xcc\x82Fup\xa2\x83\x12\x8a8\x8a0\x93\x7f\x1b\
195+\x16\x12\xe8\xef\xefGccc\xcb\xbau\xeb\x1e\xe8\xee\xeev\x91y\xfbDAA\x81^VV\
196+\xf6\x93x<~\x9f\x94\xb2\xab\xbb\xbb\xfb\xcd-[\xb6\xfc)\x18\xcf\x16/\x00\xf89\
197+\x01\x08\xc98~\xe9\xd2\xa5E3g\xce\xdc=u\xea\xd4{u]G\xbbz\x0f;\x9d\xf5P\x9a\
198+\x8f\xb8\x96\x80\xad%\x10\xd3\xf2`\x870Z\x02\x16\x8b\x0f\x87\xc1,\xe8\xd4\
199+\xc2x6\x05\xa5l\x16((\xce\x9c9\x83]\xbbv5UTT\xac\x8cf\x12#_\xd4\x14\x82e2"6*\
200+\\\x06q9@(>\x08\x06\x80WTT<;c\xc6\x8c\x9f\x17\x16\x16\x92~t\xe3\x0f\xe2?q\
201+\x91vf\xaa\xa0\xe5!\xc6\xf3\x10\xe3\t\xd8<\x0f\x16K \x16\x9eY>\xa6\xebw\xa3\
202+\x84\x97B)\x85\xb7\xdf~\x1b{\xf7\xee=\xb4q\xe3\xc6_\x8c&(\x88\\\x19\xf7\xb2>\
203+;r\x15\xca\x02\x08\xd7_-\x08\xe3\x91G\x1e\xf9\xfa\xdc\xb9s\xb7N\x992%N\xb8B\
204+\xa3\xa8\xc6\x07x\x0b6O \xc6\xf2`\xb3\xbcL\x15X\x06\xa8\x88\x8f\xc7\x97\x8d\
205+\xb9\xc8\xe77\xc1q\x1c\xbc\xfe\xfa\xeb8~\xfc\xf8_7m\xda\xb4ZJ\x19\n\x8f\x02\
206+\x88,\xd1\xe9H\xe4\x14\xaf\x94\xca\t\x10\x8ag\x00\xf4 L\x00\xd6\xf4\xe9\xd3K\
207+\x96/_^5k\xd6\xac;-\xcb\xc2\x07\xfe\x1f\xd1\xe4\xd7@\xa7\x06l\x1e@\xb0\x04n\
208+\xd5g\xe2.\xe3\x870Y\x0c\xc9d\x12\r\r\r\xe8\xe9\xe9\xf9\xb8\xb6\xb6\xf6\xe9\
209+\xbe\xbe>\'\x92\xed\xa8\xf8P\xb4\x0b\xc0\t\xcen\x16\x80\x1f\x15\x0f\xe4x\x1b\
210+\r\x00\x182\x1b\xc9\xb0\xf808\xe7\xd6\xaaU\xab\x9e\x983g\xce\xc3\xc5\xc5\xc5\
211+\xe4\x029\x8b=\xde\xaf\xe1\xd2A\xd8,\x81\xaf\x19?\xc4,\xe3{ \x84\xa2\xb9\xb9\
212+\x19MMMp]\xb7{\xf7\xee\xddOwtt\xf4b\xa4\xbf\xa3\x99O\x07\xc2\x1d\x00\xa9 B\
213+\x80h\x9f\x0c\x8b\xbf\x0c \x87}\xf4\xa8x\x00v\x00\xa4\xcf\x9b7o\xd6\xa2E\x8b\
214+\xd6N\x9b6\xcd\xf4\xa8\x8b\xb7\xc4f|Q\xff&\xa6\xea_\x81\xe388p\xe0\x00N\x9e<\
215+\t!\xc4\xc0\xc1\x83\x07W\x7f\xf8\xe1\x87\x1d\xa1\x80\x08D\x08\x10Z%\x95\x15n\
216+v\xf6UV\xc6?\xab\x02!@\xb4\x02\xc3\x00\x00\xb4\xc9\x93\'\x8fy\xea\xa9\xa7\
217+\x9e\xbf\xe7\x9e{\xa6\x18\x86\x01J)\xba\xba\xba\xb0\x7f\xff~\\\xb8p\x01\xbe\
218+\xef\x0f577\xaf;z\xf4\xe8\xc9\x1c\xe2C\x0bE\xad\x13\x15\x1f\xda(\xcc\xbe\x9f\
219+-\xfe\xb3\x00(.Y\xc8\x08\xc4\x9b\x91\xb3\x11\x8c\xf1\xc9\x93\'\x17-Z\xb4\xa8\
220+j\xf6\xec\xd9\xb1\xce\xceN\x1c9r$\xfc=\xbf\xa7\xa5\xa5\xe5WG\x8e\x1c91\x8ax\
221+\x89\x91\xcd\x1a\xb5N(~8\xfbJ)?[\xfch\x00\xa1\x8d\xa2U0\x02\xe1!\x84\x01@/,,\
222+\x8c?\xf4\xd0C\xbff\x8cMe\x8cA\x08\x01\xdf\xf7\xd5\xd0\xd0\xd0[\r\r\r\x95}}}\
223+n\xf0\xd8l\xdb\x84\xe2=\\jV\'\x12Q\xef\xfb\xa3e\x1f\x18\xfd\x1bY8\x99\xc0\
224+\xa5=\x81D\xc6}\x00j\xca\x94)\x13\x07\x07\x07\x8fr\xce\xdb=\xcf\x1bp]\xb77\
225+\x99L\x9e:x\xf0`+F\xfe\x8a\x10\xcdz\xae\xccGW\x9ep\xd5\x19\xde\x17F\x13\x0f\
226+\x8c\xf2\x9d8\xb2\x991\x8c\\\x91\xc2j\x84\x151"\xd79\xb2\xde\xe1s\x88\xcf\
227+\xf6|vd/\x999\x1b7z\xe4\xac\x80RJe\x18 \xa3\x97#\x0f\xcd\xce\xa8\x1f@\xf8Y\
228+\x10\xd9\r\x1bf=\x97\xf0\xd02W,~T\x80\x1c\x10\n\xb9\x1b1;\xb4\xe0\xcc\xb2\
229+\x00re\xde\xc1\xe5;m\xce\xcd\xea\x9a\x00\xb2 \xc2\x15 \n\x90\xdd\x94\x12\x19\
230+K\xc9\xe0\xb9\xa1\x85\xa2k}\xae\xac{\xb8\xfc]\x08W"\xfes\x01r@|^%$.\xf5C\x14\
231+ \xda\xb4\xd9\x96\x89\x8a\xbf\xa2\xac_\x15@\x08\x81\xccO\xdd\xa1\xa8\\;j\xb4\
232+I\xc3>P\x18Y\x01\x0f\x9f\xf3rv5\xe2\xaf\x18 \nB\x08\xf1q)\xbb\xd9U\x08\xb3\
233+\x1d\xf6\x00\xb2\xc0\xc2\xf3\x88\xac_\xab\xf8\xab\x06\x88@\x00\x97[*\x04\x08\
234+\x97^\x12\x19\x0b!\xa2\xfdrM\x96\xb9n\x80,\x88p\xf2h%\xc2%\x14Yc\xd1&\xbd\
235+\xae\xacG\x8f\xff\x05\xd8\xb6\xb3*\x16\xb11e\x00\x00\x00\x00IEND\xaeB`\x82\
236+\xc6\x87\x0e\x81'
237
238=== added file 'phatch/actions/warm_up.py'
239--- phatch/actions/warm_up.py 1970-01-01 00:00:00 +0000
240+++ phatch/actions/warm_up.py 2010-02-12 10:56:17 +0000
241@@ -0,0 +1,174 @@
242+# Phatch - Photo Batch Processor
243+# Copyright (C) 2007-2008 www.stani.be
244+#
245+# This program is free software: you can redistribute it and/or modify
246+# it under the terms of the GNU General Public License as published by
247+# the Free Software Foundation, either version 3 of the License, or
248+# (at your option) any later version.
249+#
250+# This program is distributed in the hope that it will be useful,
251+# but WITHOUT ANY WARRANTY; without even the implied warranty of
252+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
253+# GNU General Public License for more details.
254+#
255+# You should have received a copy of the GNU General Public License
256+# along with this program. If not, see http://www.gnu.org/licenses/
257+#
258+# Phatch recommends SPE (http://pythonide.stani.be) for editing python files.
259+
260+# Embedded icon is designed by Igor Kekeljevic (http://www.admiror-ns.co.yu).
261+# Based on Stani's colorize
262+# This action is (C) 2010 by Pawel T. Jochym <jochym@gmail.com>
263+
264+# Make a colorized version of the image with midtone shifted to
265+# prescribed color value.
266+
267+from core import models
268+from core.translation import _t
269+
270+#---PIL
271+def init():
272+ global Image, ImageMath, ImageColor, imtools
273+ import Image, ImageMath, ImageColor
274+ from lib import imtools
275+
276+def warmup(image,midtone,brighten,amount=100):
277+ """Apply a toning filter
278+ - amount: 0-100%"""
279+
280+ mode=image.mode
281+ info=image.info
282+
283+ if image.mode != 'L':
284+ im = image.convert('L')
285+ else:
286+ im = image
287+
288+ if imtools.has_transparency(image):
289+ image = image.convert('RGBA')
290+
291+ luma=im.split()[0].convert('F')
292+ o=[]
293+ m=ImageColor.getrgb(midtone)
294+ b=brighten/600.0
295+ # Calculate channels separately
296+ for l in range(3):
297+ o.append(ImageMath.eval(
298+ "m*(255-i)*i+i",
299+ i=luma,
300+ m=4*((m[l]/255.0)-0.5+b)/255.0 ).convert('L'))
301+
302+ colorized = Image.merge('RGB', tuple(o) )
303+
304+ if imtools.has_alpha(image):
305+ imtools.put_alpha(colorized, image.split()[-1])
306+
307+ if amount < 100:
308+ colorized=Image.blend(image, colorized, amount/100.0)
309+
310+ #TODO: Here we should convert it properly but convert('P')
311+ # does not handle adaptive palette and transparency correctly.
312+ # OR i do not know how to do it properly. For now the save
313+ # function will handle it sami-properly (with a message and
314+ # default WEB palette and dithering). This is a bit ugly but
315+ # at least it works as expected.
316+ return colorized
317+
318+#---Phatch
319+class Action(models.Action):
320+ label = _t('Warm Up')
321+ author = 'Pawel T. Jochym'
322+ email = 'jochym@gmail.com'
323+ init = staticmethod(init)
324+ pil = staticmethod(warmup)
325+ version = '0.2'
326+ tags = [_t('filter'),_t('color')]
327+ __doc__ = _t('Warm up or colorize midtones of an image making it monochrome')
328+
329+ def interface(self,fields):
330+ fields[_t('Midtone')] = self.ColorField('#888888')
331+ fields[_t('Brighten')] = self.SliderField(0,0,100)
332+ fields[_t('Amount')] = self.SliderField(100,1,100)
333+
334+ icon = \
335+'x\xda\x01\x1f\x08\xe0\xf7\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00*\
336+\x00\x00\x00*\x08\x06\x00\x00\x00\xc5\xc3\xc9[\x00\x00\x00\x04sBIT\x08\x08\
337+\x08\x08|\x08d\x88\x00\x00\x07\xd6IDATX\x85\xcd\x98[lT\xc7\x19\xc7\x7fs\xce\
338+\xd9\xb3g\xed\xdd\xb5\xbd`\x16\xb0\r,\xc1\x84D\x16\xc4B\xa9L\x13\xa9\xed\x03\
339+(\x15Q\x93*\x12R\x93T"\xb1\x9dU\xd5\xa8Q\x1eKT\xd2(\xd0J\x95\x8a\xf2P\xa9u\
340+\x95\xa0\x88bZT \xc5\x12\x94J\xc0K\xa3p7\x11\x11P9$\xb1Y\xd9\xeb\xa5+\xdbk\
341+\xef\xed\\\xa7\x0f\xde5\xb6\xb1\r\x0e\x17\xf7\x93>\xcd\x99s\x99\xf9\xcd\xff\
342+\x9b\x99ov\x91R\xf2\xff\xe4o\xbc\xf1F`\xa6\xfbBJ\xc9BZ<\x1e\xf7\x01/(\x8a\
343+\xf2\xaa\xcf\xe7k\x06\x1al\xdb\xbe\xe9y\xde\x96\x8e\x8e\x8e\x9e\xf2{\xdaB\
344+\x01\xb6\xb7\xb7\xaf\x03\xda|>\xdf\xf6\x8a\x8a\x8aE~\xbf\x1f)%\xb6m\xe38\xce\
345+J\xe0%\xe0\xb7\x0b\x06\xda\xde\xde\xfe\xb4\x94rW0\x18\xdc\x12\n\x85\xd0u\x1d\
346+\xc7q\xb0m\x1b\xd7u)\x14\n\x14\x8bE\x14E\xf9\xf7\xe4\xef\x1e\x19hkk\xebSR\
347+\xca\xf7\xc3\xe1\xf0\xf3\xb5\xb5\xb5h\x9a\x86\xe388\x8e\x83\xeb\xba\xb8\xae\
348+\xcb\xc8\xc8\x08\xf9|\x1eEQ\xbe\xf4\xf9|\x9f=R\xd0\xb6\xb6\xb6\'=\xcf{?\x1c\
349+\x0e\xffx\xd9\xb2e\xc2\xef\xf7\xe3\xba.\xb6m#\xa5\x9cP3\x9dN\x93\xcf\xe7\x11\
350+B8R\xca\x9fvttx\x8f\x04\xb4\xad\xadm\x99\xe7y\xbf\xab\xac\xac|9\x16\x8b)\x81\
351+@\x00\xcf\xf3& =\xcf\xc3\xb2,L\xd3$\x99LR(\x14P\x14\x05EQv\x1f8p\xe0\xdc\xf4\
352+\xf6\x1e8h<\x1e\xd7<\xcf\xfb\x85\x10\xe2\xd7\xb1X,TWW\x87\x94\x12\xd7u\'@\
353+\x1d\xc7\xa1X,R(\x14H$\x12\x14\x8bE\x84\x10H)\xcf\x17\x8b\xc5\xf7gj\xf7\x81\
354+\x82\xc6\xe3\xf1\x1f\xb8\xae\xfb\x87\xaa\xaa\xaa\'\xd7\xad[GEE\xc5Dx\xcb^\
355+\x06\xccf\xb3\xf4\xf5\xf5a\x9a&\x00\xaa\xaa\xf6\xa9\xaa\xfaBWW\x97\xfb\xd0@\
356+\xe3\xf1x=\xf0{\xd7u\xb7\xad]\xbb\x96\x95+W\xa2\xaa*\x9e\xe7a\x9a&\xb6mS(\
357+\x14\xc8\xe7\xf3\xe4\xf3y2\x99\x0c}}}\xb8\xae\x8b\xaa\xaaH)\xd3\x8a\xa2l9t\
358+\xe8\xd0\xe0l}\xdc\x17hWWW}OO\xcf/#\x91\xc8k\xa3\xa3\xa3\x81h4\xca\x9a5k\xf0\
359+\xf9|\xd8\xb6\x8di\x9a\x14\n\x05r\xb9\x1c\xb9\\\x8el6K:\x9d&\x91H\xa0i\x1a\
360+\x86a\x00\xe4\x02\x81\xc0\x8b\xfb\xf7\xef\xef\x99\xab\xafo\x95\x99\x8e\x1d;\
361+\xb6\xd1\xf3\xbc?VVV>\x1d\x08\x04\xd0u\x1d\xd34I\xa7\xd3|\xf3\xcd7\x18\x86A \
362+\x10 \x9f\xcf\x93\xcdf\' \x93\xc9$\x83\x83\x83\xe8\xbaN(\x14BJi\x1b\x86\xf1\
363+\x93}\xfb\xf6\x1d\x01\xe6\x04\x99\xb7\xa2]]]\xaf9\x8e\xf3g\xd34\xb5\xf2\x96b\
364+\x18\x06\x9b6mb\xfd\xfa\xf5\xb8\xae\xcb\xb9s\xe78y\xf2$\x91H\x84\\.G&\x93\
365+\xe1\xe6\xcd\x9bd2\x19\x82\xc1 \xa1P\x08\xc7q<]\xd7\xdf~\xec\xb1\xc7>\xb9\
366+\x1b$\xccS\xd1\xce\xce\xce\xce\xa1\xa1\xa1\x97\x01*++ill\xa4\xa6\xa6\x06\xc3\
367+0(\x16\x8b\xf4\xf6\xf6\xf2\xec\xb3\xcf\x92\xc9d\xb8r\xe5\n\x87\x0f\x1f\xc6q\
368+\x1c\x06\x06\x06\x10B\x10\x0e\x87\t\x85B\xe5\xadh\xf7\xc1\x83\x07w\x02\xde]\
369+\xba\x9d\x1f\xe8\x8e\x1d;\xde\xbap\xe1\xc2\x07###X\x96\x85a\x18,Y\xb2\x84\
370+\x96\x96\x16\xb6n\xddJSS\x13\xb7n\xdd\xe2\xc4\x89\x13l\xd8\xb0\x81|>Oww7\x87\
371+\x0f\x1fF\xd34\x16-ZD0\x18dtt\x14\xc7q\xf6\x1e=z\xb4\xfd^!\xef\t4\x16\x8bU[\
372+\x96\xf53]\xd7w[\x96%\xa4\x94\x84\xc3a\x82\xc1 CCC8\x8eC4\x1a\xe5\xcd7\xdf\
373+\xe4\x99g\x9e!\x99Lr\xea\xd4)6o\xdeLOO\x0f\x9f~\xfa)\xa9T\x8aH$\xc2\xd8\xd8\
374+\x18\xa6i\x1e;~\xfc\xf8\x8f\xe6\x03\t\xa0\xcc\xf5p\xc5\x8a\x15\xcbkjj\xfeS[[\
375+\xfb\x9bB\xa1 L\xd3\xa4\xae\xae\x8e\'\x9exb\xe2\x00\x91\xcdf\xb9q\xe3\x06;w\
376+\xee\xe4\xc3\x0f?\xc4u]\x1e\x7f\xfcq:::0M\x13\xcf\xf3\xc8f\xb3\xa4R)\xc6\xc6\
377+\xc6\xceo\xdb\xb6\xed\xc5\xf9B\xce\t\xda\xd8\xd8\xe8\xaf\xae\xae\xeejjj\x8a\
378+\x0e\x0f\x0fcY\x16\x1b7nd\xd3\xa6M\xa4R)\x06\x07\x071M\x13!\x04\x9a\xa6\x91\
379+\xcdf9p\xe0\x00\x17/^d``\x00\xc7q\xb8t\xe9\x12_\x7f\xfd5\x8a\xa20::z\xbd\xa9\
380+\xa9\xe9{\xdb\xb7ow\xe6\x0b\ts\x84\xbe\xae\xae\xee\xa3h4\xfa\xfa\xf0\xf00###\
381+477\xd3\xdc\xdc\xcc\xe5\xcb\x97\xb9~\xfd\xfa\x04di/\xc4\xf3<\x8a\xc5"\x8d\
382+\x8d\x8d<\xf7\xdcs\x9c={\x96\xbe\xbe>4Mc\xf9\xf2\xe5I\xd7u\xd7\x9e>}:\xfbm a\
383+\x16E\xeb\xeb\xeb7\xa8\xaa\xfaz\xa1P \x93\xc9\x10\x89D\xa8\xad\xad\xe5\xcc\
384+\x993\\\xbbv\r\xd34Q\x14\x85\x86\x86\x06V\xacX\x010\xfesA\x08\x12\x89\x04\
385+\xd9l\x96\xde\xde^\xfa\xfb\xfb)\x16\x8b444\xbct?\x900\xcb>*\xa5l\x05H\xa7\
386+\xd3\xa8\xaaJ$\x12\xe1\xea\xd5\xab\xa4\xd3i,\xcbBUUV\xadZ\xc5\xd2\xa5K\xf9\
387+\xfc\xf3\xcf\'\x8ej\xba\xae#\x84\xc0\xb6m\xc6\xc6\xc60\x0c\x83\x96\x96\x96\
388+\xbf\xef\xdd\xbb\xf7\xcc\xfd@\xce\n\x9a~5\xfdj\xf8D\x18/\xe7\xe1\xf7\xfbI&\
389+\x938\x8e\x83i\x9a\xe8\xbaN}}=\x9e\xe7\xd1\xdd\xdd\x8deY8\x8e\x83\xae\xeb\
390+\xa8\xaa\x8aa\x18\x08!X\xbcx1\xb1X\xec_\x9d\x9d\x9d\xdb\xee\x17\x12f\x98\xa3\
391+\xe2=Q\xcdw\x19\x16\xff\x14(\x8d\n\xb2AR\xf3N\r^\xc2C\xd7u\x82\xc1\xe0\xc4\
392+\x8a\xb7,\x0b\x00\x9f\xcf\x87\xa6\x8d\x8fy\xfd\xfa\xf5,]\xba\x14]\xd7wutt\
393+\xfc\xeaA@\xc2L\x8a*\xac$\x012 q[]\xf8+\x0c\xad\x1d\xa2bI\x05\xd2\x95\xc8[\
394+\x12/\xe5\xe18\x0e\x9a\xa6\xe1\xf3\xf9\x10B\x00\xb0z\xf5j\xa2\xd1h\xae\xa6\
395+\xa6f\xfb\x9e={\x0e=(\xc8\x99A%\xab\xb8\x06\xbc\x02\x1c\x01\xce\x82\\\'\xc9\
396+\xa99\xc8\x80\xfa\x95J\xf5\x7f\xab1\x0c\x03EQ(EDVUU\xfd%\x1a\x8d~\xf0\xf1\
397+\xc7\x1f_~\x90\x80e\xbb3\xf4\xef\x8a\xfd\x14y\x85\n`\rP\x0f\xf4\x00\xdd\x80\
398+\n\x84@1\x15\xaa\x8eW\xa1\ri\xf8|\xbe\xcf\xa4\x94o\r\x0c\x0c\\|\x18\x803\x82\
399+\x8a\xf7\xc4b\xaa\x18d\t*\xd5@\x02\x18\x00,\xc0)\xb9\r\x98\x10\xbe\x1a\x1e\n\
400+\xf6\x05\x7f\xde\xdf\xdf\xff\xb7\x87\tX\xb6\xe9\xa1o\x11\xcb\x85-\x13R\xe5Kn\
401+\x1f\xbe\x8c\xf1ka\x8ba\xe1\x89^\xc2\x9c\x1f}~\xf4\xdd\xcc\x8eL\xeaQ@\xc2\
402+\xcc\xab^\x01\x9e\x02"\x80\x8e@ \xe9#\xccW\xf2mYxT`\xd3m\xc1\xff{\xbaW\x9b\
403+\x92B\x05B\x08G|\x7f\x81X\xe6\xb4\xa9\xb9>E+C|\xb4@,s\xdaT\xd0\x02\xef\xe0\'\
404+&\xae\x88\x96\x05\xe2\x99\xd5&@\xc5\x17\xa2\x06\x8f\x95\x84\x11d\xd9\'\x10s\
405+\x1e\xaa\x1f\xb5\xdd\x86I\xb1\x99\x1b\x08\xfa\x81\x9b4\xf2\x0fv-\x1c\xd6\x9d\
406+v{\x1f\x1d#O/\xf0\x05\xe0\x02\xd5|g\xbe\x8d\x95s\xfe\xc3\xd8I\xa6f\xa6?\x89#\
407+\xa4\xf9!\x01\x12\x84x\x8d8\x17\xca\x8f\xa6\x95\xccR\x87\xf14!\xef\xe1zJy\
408+\xb7\xc1\x89\xd2\xc9\\a<\xff\xf8XL\x804*\xa03\xae\xb8(\xb92\x0bp\x19\xc0\x9b\
409+T\x96\xdd\x9d\xa1\xee\xce\xf2\xac\xec\x13mN\x11\xb1T\x06\x81\xb5\xc0\x12\xc0\
410+_\x82R&\x7f4\xcd\xa77>\x1d`.w\xeeR\x9f\xd2F\x99\xb6\xacX\x00XTr\x7f\xe9\xfed\
411+\xd0\x99\xa0\xee\xb5\xf3\xe9\xd7\xda\xb4{\xea\xa4z\xb9\x9c,\x90\x0b\xb7C\xef\
412+\x03\xc2%\xaf,y\xc5$u\xcb\xe1\x87\x99C<=\xd4wS\xf5n\x83r\xca\xfd\x94\x15\x9d\
413+XL\xa5\x15\xab\x96F\xac\x96|\xfa\xbc\x9c\xcb\x99T\x96\x95\x98i0\x93\x07:\xd3\
414+b\xbbc~\x02\xfc\x0f\'\xc4\x97\xb6\x97\xab\xbe\x05\x00\x00\x00\x00IEND\xaeB`\
415+\x82`8\xef\xcc'

Subscribers

People subscribed via source and target branches

to status/vote changes: