diff --git a/Images/lena.fli b/Images/lena.fli new file mode 100644 index 000000000..2c7367e22 Binary files /dev/null and b/Images/lena.fli differ diff --git a/Images/lena.ico b/Images/lena.ico new file mode 100644 index 000000000..6fadbe29e Binary files /dev/null and b/Images/lena.ico differ diff --git a/Images/lena.psd b/Images/lena.psd new file mode 100644 index 000000000..c7b19402a Binary files /dev/null and b/Images/lena.psd differ diff --git a/Images/lena.tar b/Images/lena.tar new file mode 100644 index 000000000..3afb1f542 Binary files /dev/null and b/Images/lena.tar differ diff --git a/Images/lena.xpm b/Images/lena.xpm new file mode 100644 index 000000000..925fd03cb --- /dev/null +++ b/Images/lena.xpm @@ -0,0 +1,175 @@ +/* XPM */ +static char * lena_xpm[] = { +"128 128 44 1", +" c None", +". c #CC9966", +"+ c #663333", +"@ c #996666", +"# c #CC6666", +"$ c #CC9999", +"% c #FFCC99", +"& c #996633", +"* c #FF9966", +"= c #663366", +"- c #FFCCCC", +"; c #CCCC99", +"> c #FF9999", +", c #993333", +"' c #333333", +") c #CCCCCC", +"! c #996699", +"~ c #CC6633", +"{ c #666666", +"] c #999999", +"^ c #666699", +"/ c #330000", +"( c #663300", +"_ c #330033", +": c #999966", +"< c #FFFFCC", +"[ c #333366", +"} c #660033", +"| c #CC99CC", +"1 c #9999CC", +"2 c #660000", +"3 c #FFFFFF", +"4 c #CCCC66", +"5 c #FFCC66", +"6 c #666633", +"7 c #993366", +"8 c #FFFF99", +"9 c #993300", +"0 c #333300", +"a c #FF99CC", +"b c #CCCCFF", +"c c #CC9933", +"d c #CC6699", +"e c #000000", +".....*...*.%*..#&@#&@&#&#&#.&#.#.#.#.#.#..#.*.#...#..#.#.#.#..#..#..#..#..#.#.#@#.>..>...*..*...*....%%%%.&&.@.#.@.#..&#.~.#&#$.", +">%*..*.*..*%.*.&#@&#@#&#&#&#*&.#.#.#.#.*#..#&.~.#.#.#.&.#.#.#.#.#.#&.#..#&.@.@&#&#.>.*.*...*..*..*...%%%%%&&~@#&#.@&##.&#.#.@.>@", +"...>...*.*.>.%*&#,&#&#&@&#.&#.#.#.#.~.#.#.~.>.#.#..~.#*.~.&*.#.*&*.#..#&.#.##.#@&#.$.>.>.*..*..*.....%%;%%@&@.#.@.#..&#..&#.#@+(", +".*...*...%*.*.>&&@@#@&##&#.#&#.~.#.#.#.#.##.#&*.~.#..#.#.#.#.#.#.#.#~.#.#.#&.#&@#&#.>.....*..*..*.*...%%%%%&.&@.#&#.#.#.#$.$&+++", +"..>.*.*.*.%.>.~&#&#&#.@&#&#&.#.#.#..#.~.~.*.~>&>.#.#*.#.#.~.~.#.#.~.#.*&*.#.#.#.#&#..>.>*..>..*.......%%<%%$&#.#&$&#.&#.&>@,++2+", +".*.%...%*.*..*.#&@#@&##&#&#&#&#.&.#*.#.#.#.#*.~*#.~.#~.~*#.#*#.*.#.#.#.#&.#.#.#&@.##.*..%*..*..*>.*.....%%%%&&&.#~.#.#.#.$&+++++", +"..>.*.*.%*.*..*#&,&#@&#&#&#.#.~#.~.&>.~.~#*&#~#.~>#.~$~$&.~..#.#~.~.~.#.#.#.#&$#&#&$.>.>..%*.>....*.....%%%-.@#&.#&#.#.$$,++++++", +"..*.*%.*%.*.>..#&@#&##&#&#&#&.#.#.#.~.#.*.#.*.~.~.#.#.~.##&#.~.#.##.#*&*~.#.#&#.@#&>.>%.*>.%..*.>..*.*..%%%%%&&&.#.&#&#.@++++'=+", +".*...*.*.*.*.*.~#@&#@&#&#&.#~.#&*&*&#.~#.~*#~#.#*#.~.#&>..#.#.#...#.#.#.#.#..#.#&#.#..>...*.*..%*..>...>.%<%-%@.&#.#.$@@++++++=+", +".%.*%.%*.%.*.*.&&,&#&#@&#&#.#&.#&*#.~.*.#..#.*~.#.##..#&..&*&#.##.~.#~.#~.#~#.#.@#@.#..>.>..*.*..>.*.*....%%%%.&&#.#&$,++++=+='+", +"*>.*.*..**..*>.#,@#@#&#&##&#&#*&>&*#.#~.#~.#.#.#*#.&#&.#@#.@..#..@.#..#.#~..#.#&#&#.>.>...*.$..*.*...*.*.>%%%-%&&@&$#@++/++'++=+", +"..%*.%*...*.*.*&,&#&@#&#&&#.#.#.#&.~#.#.#.~.#.#..#$#$.$...$&@.@.#.@.##.#.##.##.#.@#.#..*.>.%*.>...>.#.#....%%<-.&#.@&+++=+=+=+=+", +"*.*.*.*.*%*.*.*&#,@#&##&##&~#&*&*#*&*&*&*@.#.~.#$..$.$.-$%$-%$.@.@.&..#*#.#*&#&##&#.#.>...*....*>.*.*.*.~..%%%%%@@.@++++++=+=++=", +"%..%*..*.*.*..*&&&,@#&##&#.#.#.#.#.#.#.#.#.#.#..@>@.$$$;$%$.$-$%$.#@.#..#.##.##.#@&#$.>.>.>.*>.....*.~.*....%<%<.$+++/+=+=++=/++", +"*.*..~.~.%*.*.*# #&,&#&#&#~.#~.~#.#*&*#..@.#.$.$$.$.$;$;$%$%$-.$.&@#.#~.##.#&##.#.#....*...*.>*.#..#.~....<%%-&+++++'=+[++=++", +".%*.*.&*.*%*.*. @#&#&#.#&#.##.~.#.#..>.#.$&#$@.$$:$$;$;$;$;-;-$.@&#$#.#&###&@.#..#.>..>.>..~..*#*.*.~...%%<-@+++=++=++=++=+", +"*.%*&,&*.*.*.* @#&##&#.~&*&>~.#.>@#&.@#.@$.@$.$.$.$;$;%-;.)%-%$&@.##.#.@.##&#.>.*.#.#.*&*.*.*.#.#..*...-%-+++/+{++_++=+}+", +"*.>.~&~..%*.*. &#&#&#&#.##&.#~#&*&##.@.#&$#.@.$.$;$.%).;-;$;$)%$&@&@#.##,@@.#..>.>..>.>.~.*.#.#>.>.#.*.-$+/+=+=+=++=+=+_+", +".*.&#(#.**.%*. @#&#&##.#&*#.#..##.#&#@.$.$@>$$.$.$%]%;$;;-;)%)%--&@&@&#&#&##.#..*.#.#..>..~.*..#.*...#..++='+++=++=+'+++@", +"%.#&~&~..*c*.* #@&#&#&#.#&*&#*#.&#.#.#@##.&.@.$$.$%$$;$%]%)$;$;-<-%@&@$&##@&#.>.>.#.#&#.*.*.*.>*.~.*..>++++=+=++'=+++=++$", +"*.~&,&#.*%.*.*. &#@#&#.#&#.#.#.#&#&#&@.#..@$#.#.@.$.]%;-;-;$;$;-;)--<$@#&,@&##.*.*.>.#&#&$..*.#.*.#.*$.$&++/+++='=+}+=++@$$", +".#@&,&#.*.*%*.*& @&#&#@&#.#.#.~&*.#&#.##&@@.#.@.#.$.$.$.$%$;$)-3))-;;-;-&@@@#@#&#.>.*.#.@(,$#...*#.**.*..@++++!'+}+=+=+@+@@$.", +".&,&#&&>.*..*.#&#@,&#@&#&#&#&#&#.#.##&#&#.#.#&$.#.$.@.$.$$-$;$$%)%-;-;3;)<-)&@&@&,#.~.*>.>#&,+.@**.*.~>.#.>.+++'+++=+=+=++@@@.$.", +"&#&#&,#..*%*.*.,&,@@#@#&##&#.#&#&#.&~#.##&#&#.#$.#.>.#.$.;.$$;-;-)-);)-)<)%3-++@@&@#.*.*..>@,+@@..#*.*~**.>+++_+='++=+=+=++@.$..", +"&,@,&@&>.*.%.>.#&+&#&@&#&#&#&#.#.#.#&#~.#.#.$&#.#..#.$.$.$$.-;--)%)););););-<-&+,@~@*.**>.#&+2+@>*.*#.*.#>&2+++=++=++_+++@@$$...", +"&#&@,#&>.%**.*.&,@,&@#&@@#&#.#&#.~#.#&#&#&#&#.@.#.$.@.@.$.$;-$-;-;-)-;-);-);-)-$+@&#.*..*.>@++++@.#.**.>*$,+/=+=++'=+'+=+@$$.$..", +"@,#&@&#.*..%.>.#&@@,@##&#&##&#.#.#&#&#&#.##.#.$.$.#..#.@$.--;%)$-)%));)<))<)-<)%@+@.#.**.>.#++++#@.>&#.>.@++++=+'+++=+++@$.@..-.", +"@&@,&,&>..**.*.#&,&#&@&@#&#&#.#&>&#&#.#&#.#.#.#.#.>@.@.$%).-$;-;$-;-%)-)%)-;)%)-$+@&.*..*.#@+++++.@...$.#++}=+=+=+=+++++$$$.$...", +",#@,@&#..*...>&#@+&@,@#&#&#.#&#&%#&.#&##.#.#.#.#...#&$.-.-;$;$-;)-))-;-;-;)-;3;3-@@&$.*.*.>&++++/@.-%%$$+++++++=+'+=+++$.$..$.$.", +"@,&#@,&#%.%*.*.&,@,&#&#&#&#&#.#.>&#&#.#&#&#.#.@.#@@..$%$%$.-;$;-;-;-;--)--;)-;);-$(#..*.>.>@++/++--;-<<)/'+[+=+++=++'+@$&$.$%.$.", +"@,@&,&..*.*%..>&,&@@,@#@#&#&#&#%>&#&#.#.#.#.#.#..&@.$%$%]%).-;$;-;$)%$;-%)-;))<)3-@@...*..#@++=@-)%)%)-<{+[++{+=+='}+@:$.$..$.>.", +"@&#@#,&#.%.*.*.&,@,@&&#&@#&#.#&%>&#&#&#&#.#.#.@>&#.$%$.-.$;$;-;$;$;-)--)-;-)%);)))%@..*.*..@++]-]$<);<)-]'='=++++=@++$.$..$..$.$", +"#&,&#&#.**%.%*#&,&&##&#&#&##&&#%.#&#&#&>&#&.#..#&-*$.$.-$-.)$.-$;$-.;-;.-)%;);-;-)).$...*>&&+$)$))--;)%)$+'+=+=++'}'$$$.$.%$....", +"#&@#,~&*...*..*&@,@&&#&##&#.#&.%*&#&#.#.#.#.&#.&.>%.$.$.;-$%$).-$;-$;--);$);-);)-))-$.>..~@.%;;-;));-;3-{+'+++=+=++@@$$..$...$$.", +"@&#&##&*.*5.%*.#,&@,&#&#&##&#&>%#&#~@#&#@.##.@.>..#.$%$-$.).$;$;%$;-.;-%)%$;-;-;);-;%$....%--$3)-;-));-)++=+[+/++/+@$.$$..$%>..>", +"~@#&,&#*.%*.*.*&@,&#&#&#&#&#@&%.#&#&~.#&#.&#.#..>.$.%$.%$.-;$.$$%$.-;$.$;$;$;-;-)$)-%$.$-%-;%;-;-)-;-3%)'+'++=+=++@$$$.%$..$..$.", +"@&###&#.*.5.%.>&,@@&#&#,&#@~&#%..~#&#&#&#$&#..>..#.>$.$.$%$.-;%.-.$%$%)%$;-)%$);%).]%-%%$.-;-);-)%)%;-3-+'=+_++++@$$.$.$..$.>..$", +"~@&#~#&*..%**.*.,(#,&#&##&#&#&%.>.*&#&#.#.#.#.#..#...$.-.$.$..$.%;$.%$.-;$%)$;$$;$;-;$)%-;-$%)-;-;-)-;-]++'+'=+++@.$$.$.>...$.>.", +"#&#&,&#.*..%.*.#@@&@&#&#&##&#&-%..#.#&#&#.#.$..#.$.>.$.$.-;%$%.-..$;.).$%$-.-$;$;$;$;$;-);-;)-;-)%$%)%-{+_=+=+'+@$$..$.%$.>..$.$", +",@&##,~*.5*%*.*&,@,##&,&#&#@&~%.**.~&#&#&#.#.#..>.$.$%$.$.$.$.$.$%$.$%$%$;$:-$-.$%$;-;-;$)-)%)-;-$:$.)-+'+'=+++@@.$.$.$...$.>.>.", +"&#,&#&#.*..%.*.#,&@&#&#&#&#&#&%..*&*#&#&>$.#$.#..$.#.$.-$.-.-..-..$%$.-$;-$$%]$;-;%)%))););$)%)%-.$@;$$++=++=++@$.$.$..$%.$..$.$", +"@&#&#&#..*.*.*.&@,@#&#&,#&#&&#%.*.*.&#.#.#.#&$.#.>.$.$.$.$$.$$$.$$$%$$%$.$;$$.$$;$%$;)%)-);-;-;-.).$$-'+'+='+'@@.$..-.>.$...$...", +"&#@,#~#*.%5%.%*#+,&@,#&#&##&#.%*.*.*~&#.>@#.>..#&$.#$$$$$.$$@@$$$.$$$$:$$-.$;-;-%;)%)$;);-)-;--$;@.@;-++=+=+++@$.$.$..$..>$..$>.", +",&#&#&&*.*.*.*.~@@,&#&#&#&#&,.%%..*..#.#.#.#.>.#.$#..$&@@@@@$$@$-$$]-$$+.{$%-$;-%$;-;-;-)-;--;-.$@:@-$/++_+=++@$.$..$.$.$..$..$.", +"@&#,##~.*.%.%*>&,@@,&#,&#&#&,.*.*.**.&#.#$#$.#.@*@.$>@$$@@@+$)@!]+@@@++{+$;$:-%]%)%$;-);;--;-%$@&+@%-+'+=++++@$$.$.>.%.$.$..$..$", +"&#&&#&#*..5*..*&,@,@&#&#,#&#&&%%%*.*.#.#&#.#.$.#.$#.@$@@@{!@!@!+=$=@'+++@$$-$%-;)-;).)%-%)%$.@&@&$%)+++'+==++.$.$%$.$$.$.%$.$...", +"#&#@~#..*.*%%*.#@+#@#&#&&#&,&~%%5*.~.~&#.$#$&#..#.$#$@$@!@={=!!@[@[@=e+)$;-;);$%;$-;-);$<$&@6+@&]$-(++++=++++$$.$.$%.$..$....$..", +"&#&#&##*.%..*..~#+&#&,###&#&#(%%%...>&@#.#.#.#.@>.@#$@@+@+=+!{=!!=+!++)@-]%$%)%$;)$;$%)%@]&@&@.$.)+++'++=+=+@$.$%.$.$.-.$.$$..$.", +"@#&##&#.**%.%*.#&,@,@&#&,#&#&#.%5....#@>#$&#.$#.$$@$@=!==+=]!_+=@[+{+$$)%$);$;;-$-;---@@@@.:%.-$-++++=+=+++@$.#.$.%.$.$.$.$.$$.$", +"&&#&#~#...5*%*.&,@,@#@#&#&#&&,&%%%%..@.$.,.#@&$@,$@+}@='=/={+=]!_!/!$]%$;).-$;$;)-$;.@{@@$-$..%%+/'++=+'=++@$$.%..$.%..$%$.$.$.$", +"#&#&#&#**.*..**#@,&@,@#@#&##.#&%%%..#$##&#.&$.$$+@+!@=+!={=+={=@@_]$@-;$%$;$;$;$;-.$(+@${@:%.--+++++=+=++++@..$.$%.$.$%$.$.$.$$.", +"#&###&#.%.%*%...,@,#&#&,#&,&#&#&%%%.&..#$#.>@#@@@@=@=+=!+_'+=+='!!{@)$--;$;$;$%;$)%]&@@@$@$$%.(+++++'+_+=+@$.$%..$%.$..$.%$.$..$", +"#&@&#,&**.%.*%*&,@&@,#@&#&#&#&&9%%.>.#@..@.@.@@@@@@=_=+=@_+=@@!+!]@$).;$.-.$;$;-;-%-.@@@@@:-@++++++=@+'+++@@@.$.%$..$..$.$.$.$.$", +"#&##&###..*%.*.~@,+#@&###~#&#.#&.%%.&.,.#&#$@$@+=+@=+=^@[+++=++!@]@)$%).).)$.).-;$;%.+@@@$@.6+/++++++=+=+@$&$%..$.$.-.$..$..$...", +"&#&#&,&*.%.%*%*#&,@&#,@&@&#&#~&#&-%#.#&#&>.@@@}+=+={!==@!+'+'+@+$1$)$%$.$$.-;-%;-%-%$@++=@]$+/+=+{=+=/+++@$$.$.%.%..$.%$.%$..$.$", +"#&@##&#.*%*.....,@,@@&##,#&#&>&#&%%&#&.@.@>@@+!+='+==+![$!+++@@$]$-$;$;$%$.$;$%-;-%%%$++@+$]+++=+++++'+++$@$.$.$.$$%.$.$.$%.$...", +"&#&#&,&#..%.%*>&,@(##@#&#&#&#.#&#->.&#&>&@++!+=+=+=+[=+=@++/@$$$$]$).$..$;$-%-;%-%;%.$@+}{$$+'+=+={+=+++@@$.$..$...$..%.$.$..$.$", +"&#&##&#*.*%*...#&@,@&#@#&##.#~&#..-&#..#$@@++++^+==+^=@!'=/@$$$-$$$-;-.$.$%$%$%-%--%%#@+}@]$@++=++=+++++$@.$.$..$.$.$.$;..;.$...", +"@&#@#,&...%.%*.#&@,@#&#&#&#~$~#.##%#.@.$@++!_=@={+=+=='=+@{@.$;$)&$%$.$.-$%$%-;%)%%%-%@+++@${+={=++=+++@$$.$..:.$.$:$.$.$$.$.$$.", +"&@&#&,#*.*%*%*.&,@@&@#@##&#&#.&#&$@.@.#@+!++@'+=+'=+=+=@'@$:$.$%-:-$.$.%>%$-.-%%-;-%%>@@+/@$++=+={++'++@@$$.$%$.;..$..$..$.$:..$", +"#&#@#&@....%.%>&#(@#@&#&#&#&##*#@&#.#$@@+@_!_=+{=+={==+/+@@$.$;$$$..$%>.$>%$%-;.-%%)%-$+}+@)@++={+=++++$..$;$...$$..$:@.$:$.$$$.", +"@&#&#~##..*%.*.&@@,&#@#@##~#.&#.>++&$.@+=+=@=+=++++='+^+]@-;-..-%$@&&#.>%>-.$%-;-)%%%$#++++-@+@+=+[+++@$@.$..$;..$.$.$.@.$$.@.$$", +"&#@,@,&*.%..%%>~@(@@&#&#&@&##.*.#+2$@@@+!=^!'={+_+=@!@_+@:-.).;-.$.$.,(#.>$%$%%%$;-%.@@++++%@+=+{+=+++@$$.$....$;.$:.$.$.$.$$@.@", +"@&&#&#@#.*%.*..&#@,@#&#&#~#&#&>.$@(@@@+@++=!+=+'@/=/@+'$@)$%$.-&@$$$.$&,&#.>.$;%%$@@&@#@+++]$++=+=+'++.@.$%$.$..$%.$.$.$:$$$:$@$", +"##&#@9&..*%*%%*.,+#&#&#&#&@&>..>.$@++!=^!!/^+^+=1'+=++@:-.;-$%@@@+++++$.#.>.%%-;-$@+++@++++$@@++=+=++@$@.$.$..$..$$.$.$.$.$.@$:$", +"&#&#&#,>...%.*..&,@,&#&&#&#..$..$+@+=@!@^]=@=+'+_!'+0+$.$;-%-+(+,++@@2+#.#*>.%-;@+++++}+++{$@@+++'+++@..$.$.$..$...$.$.$.$$$$$@$", +"&,@,@(&.**%*.%*&,@,@&#,.,&#-.$.@$++^+^_=!@_'+==/^+_++@:.-.-%+@,++@++--(@~.#.>%%$++@@a+++=++$]@=+==+++.@$.$.$.$.$.>$.$.$$.@.:@@.@", +"#&#&#,,...*%.*..@,&##&#&#@+,$$$(+/@=+=!+^1+[+'+='=+'+@$$.%-&@&,&@+@@$-#@#&#..)%6@+@@$7++++@@$@@++=+++$..$.$.$..$..$.$.$.$.@.$:$.", +"@&#@,&@#.%.%*%.~#,&#&@.,+&@--%+++{!'=!=^+!+==+=+=+'+$..$;-&&$$@@&$.--%.$~#*.>%-@.$#$$,@+++!{$@@+=+'+@:$.$.$.$.$.$..$.$.@$:$.@..;", +"#@#&@,&#.*5.%*..&@#&##.+#$$$@++@@'}'={=!!!'/'=+'=+++@-$%-&@...*$#$@&@$-.#.#.$%;$.$.@,+@}++@@|{++++++@$.$.$..$....$.@.$.@.@.;.%%%", +"#&@,@,&#.*%*%.*.,,&#&#$#$$+@=+'+1$!==^={$1+=++=++/+$]$.-&&*#>.$.$.$.-.#.>.#*$-%-$.##@#@++++@$@+=+=+@.@...$.$.$.$.$.$.@.$.$.$%;.%", +",@#&,+,.>.%*%.*.&#&#.@&$@!+!+==@!{^'==!!!!{+='/='+@@$--#&#...>.>.$.$.$%#*#.*$%-.$.$.#$,@+++@$@@++++@$.-$..$.$.$....$.:@&$%%%%%.%", +"@&+@,&+...%.*%.#,#,@##$#$$|={={^{!+_=]={@$!+++=+++@$$%$&@.#*>.>%$%%$%$..>.#.>%--.*>##@@+}+{+|$+++++@....$..$...$.$.@.@.;%.;%.;5%", +"@,&,@+,#.*%*%.*.&,&#.$@$+=+='=!+!{[='==!^/@!'++/+@]$--(,#.~.*..>.>.%$%>.*#*#$%-%$...>##+++++$]+++++$@.$..$.$$.$.@:$...$.-%.%%.;%", +"@,@+,&+...*%*%.~#&,@@#$+!@^=!'!1'_+==+=+++]@+=+++$$$)++&#&>~>.>%%%$%..$.*.#..---..>.#$#+++++-$++++@.$...$...$.@...@.@.%;%%;%%%%%", +"@+@@,+,@>%..%*..,#&#&@@$!{=!==!{=='+[+{=++$@@'/+$&--@+/@~#*~.>.$>%.%>.>.>*#~$%%-.>.#.#@,++++$$++++@$.$..$.$.$.:$.$.:..%%.%;.;%;%", +",@(+,+&#..*%.%*&,@#@@$$+@!]={^@!{+=='_++++$@$@+/@$-)+/@@#.~**.>..>%$%..*#.~.#$-%...>$#++7+++1$++++@.$.%..$.@.$.@.$&$.;%%4%%%%;%%", +"+,@@+,+&>.%*.*..&@&#$$+!@^===!1]|'+=+}{@+@$$+++]@%$+++/&###~#..>...>.>..#*#..%--.>..>@,+++++$$++++$.$.$...$.$.:$.@...%.%.%4;%%;-", +"@@+,+,+..*.%*%*#,,@@@$@+!{={1{=$=/'+=+@+]@+@/'/$@%@'++@@~*~**>.$*>%..$*.~.#*@.-<..$#.@+++!+/$$@++@$....$...$.$.@.@.$%%454%%%;-%)", +",@+@,++#$%*%.%..,@.#@@@=@!==!=^][+='/=@$@.++++$@:$+/+/@+@#*#*.*$..>.>..##*.#..--...>@,}++@+/$$$+(@@$.$..$.$.$.$.$@..%%%%4%;%;%)%", +"@,@++(,....*%*..#,@@@$+@!{=@^+!!{|+={++=+@+/+@;$-@+'++@,#&*#.#.>*>.%>*#.*#.#.$%-.>.@$+++=++++$$/+.$.$.$..$.$:$...@.%%.4%%%;%%;%;", +",@,@,@+.>.%..%*&&&,@@@@!@=_!='!!]1+={}'+!++++$-$]+/+++@@&#*~*>.>.>.>.$*.#&#&#&>.*..#@+=++++++$$+&$..$.$.$.$.@.$.$..;%%%4;%%;%-%%", +"@,@,+&@...%*%*.>#+#@$@+!+=]=!=]!|!'+!@/++/@@@$6-++=+/+#@~#.#*~>*.#*.*.>.*..#.$%$.>$@+}+=@++++-@+@.$.>.$..@..$:@.@..%%4%%%;%;%;;%", +"@+@@+,@.$*.%.%..&+&#@$+{!@=_{=!!{!!'@^++!@$$@@$+'+++++#@$~#*#.#.>.>>.>.#.>.%%-%$..#@++++!+@+($$@#.$.$..$.$.$..$..$.%%%;%;%%;-;-;", +",@,+@+#..%.*%*.##,@@$@!+!{==={={=1@={@/+]$$$@@@+++=@++@&#.~*~**.#...*..>..>-.%>.>$.++=++@+++/$.@...$.$..$..@.$.@..%%.%5%;%;-%<%;", +"++@+,+@.>.%.*%..&@@#@$+@!+!'==!!^!1+@'+@@$-@@]+++@++++#+##.#.#*.*.>.>.*.>..%$%%$.$@/+=+7@@++($@@$.$...$.@.:$.:.@..%%%;%<%%<;%-;%", +"+,@+@,&$..*%.*.#&,@@@!+!+[=@^@[+^={|++.)$-+@$++{+=+!+++&#.~***#.#*.~.#..*$*>%$.$#@+++++=+@,++@:....$.$..$.$.@.$.@.%%%5;%;;%;%;-<", +"++,++@,.>%.%%%.#&@#@$@=+!'===!{=^!!{@)@-%@@@+/+@}++++++@##.#~.**.#&#.~#.#&.#.,&@>$++++++}@++(@@@.$....$...$..@....%%%%%%%%;-<%;%", +"+++@+@&$..*%*%*.@$,@@@@=+={^'|!{!!{+@$&3$&]$++=+++=+=+,+&#~.#*#.*>*~#.~~>&#.>$.$$}+}'+=+@@+@+&$.$..$..$.@.$:$.]@&%%;%4%;%;%<-;%<", +"@++++@&>..%*.%.#&$+$@@'=+_=@=]=!^!={]$$;@$$++'/=+++@+++@@&#~.~.#.#.*#*#.*>%-*#$>@+++++++}@2@+&$.$..$..$:.$..$..$&%%%%%<%<%;%%;%%", +"++7+,+@..>.%%*..#+@@d+@='='=^@[@^^@!$]$$@-+++++++=++=+@,,@~@~#*~.*>.~.#*#.>.>.>@+_+=+++@+@+@2+$..$..$..$.$:@:$@:@;%;8%;%;%-;%;%;", +"=++++@&#.%.*%>..,@$@@+!+=$[@b{!{=^!]@$$$-+@+}{=+++++@++@,@#&#.#.#.#*#*#.#~##$#$,+=++{++@7@}@++.$..$..$.$.$.$.$.@.%%%%;%<%;%)%%%%", +"+++++#@..*%.%*..@,$@@+!'_=='|^@1!+1@@{$-@++=++++++=+{+@++&#&#&>.#*.>..#.>..>.>@@/=+++++@+@+@++.$...$..$.$.$@:$@:@;%<%%<%%<%%;%;%", +"+=++@@@#..*%.%>.@+@@+@@='@[=$^!]!^+1$+]$+++++=+=++++=+++++,#&##.#.#.>*.>.>$%$.++=+[+{++@+#+#++$.$..$.$.$:$.$$$.@&;<%;%;;%-<%%%%%", +"+++++@..>.%*%..#@,@@=+^+=^+^]='1|]=]$@@+=+=++'+++++=+'+++&+,,&#.#....>.>%$.>$#(++=+++++@@+7@@(-.$.$..@.$.$@:@$@].-%;<%<%;%%)%;%%", +"+=}++@@...%.*%*.@+@+++!+'='_!]={!!]!!{!+'=+=+='@+++++=@+++(+(,#&#.#>..%.>%$.$@+++'={'++@+@+@@+.$..$:$.@$.$.$.@:@:%<%;%%<%;%%%%..", +"+++++#@#.*.%*%.#+@$@=@==+='=]|={!^|{|$1@_+'}{@}@_+'+=+++@+#+,+,(#&&#.#>.%$.$.@++=++++++@!@+#++%$&@.@.$.]$$@$]@$&.;%%4%;%%%-$$+++", +"++++++&$>.>..*..@-+$+'@={=+{!]=={!^!]!{)!'=++=+@+=+++=+=++++&#.#.#.>...$*.$%$.@+'='@{=+@+!+@@($.@.@&$@.@.@.$.@]@%%<%%%%;%;.(++++", +"++++}@,&$%*%*$*.-++===+=+=+_{|]=[!$^@^!+]@'=@++=+++=+'+@++,@#@#$#.#.>.*.$..$%$;-@+'++++!@@@+@($.&@.@.@@@.]@@.@&$;%%4%8%%%.@++++@", +"@@+++++#.>.%.%>.+@]++_{='!{+[@^@|]^!^@^|]!++='+@=+'=++=++++@@#.#.#.#.#..>.$.-%----]/'++++7@+@+.$.$&$&@&@@&@@@@@&%;%%%%%;$&(++@@@", +"@!@@+@+&..**%*..@++='+=@_+^@='|^!!{^@1@^$+1+@+=+++++=++=@++,@#.#.#.>..>...%.-.-;-)--+++@+@@+,@.@.$.@.$@.@@&@&$&@%;%;%;%.&+(@,+@,", +"$]@@@++,.$%..%>.$@++_+|{=]+^+={={==!{!]!{={{=++=+=+'=++++@+@&#$##.#.#.~.>.$.%%$%%)%3;@'+++@++@$.@.$.$.@.@@@&@&@&%4%%%%$(+(@&@,@@", +"@$$$@@2@.*.*%*..$@+=+_+=@_@'!{=$={'!^$!]=$_$+'/@=+=++'=++@+,@.#..#.*.>..*.%%$%.;$%;-%-$+{+@++.$.$.@.@.@$&$@&$.@&%%<;%%@(@@@+&@&@", +"{$]$@@+@$%.%*.>.@)/'={=+='=+=+[{==!{!{1@1+)=+=+{+'+=@+++@@++#.##.#.#..#..>...$%%;%-;%<)@++@++$$.$.:$.$.$@.@@&$&%;%%%;.@(@&#@#@,@", +"+@|$$@(,..>%*%..@@+/=+='=+{='=+={[+=!!]|]!+{$+/=+=++=++@@@#(@#&$*.#.*#.*..%.%.%-%;%;-%--{++++$.$.$@.@$@.$#.@.&$%%%;%%@(@@,@.@&&&", +"'@]$$@++...%.%*.$@+=/!+=+=@=+_{==@^{+^!@|{=|)=++{+=+{+}+@@@(,.#.#.*>.*.>.>..-..%$%-;%<;--+@++.@$..:$..$.@.@.#..%%%%%.&,@&@#@,@#@", +"++]$$$+&.*%*%*.$$+'=+[='_'$'='_@^{!!!{!]|'=@-+/!+=++=+++#@#2@.#.#.#.~>.*..>..%$%;%;%-;-%)++++$..$..@$@.@$.@&..%%%<%%$&@&#@@&@&&@", +"''$]$@++$%.%.%*.$++'=+=+=@!'+=+='=+{!'!1!@={|'+@{=+++++@@@&2&#.#*~.*..#.>.%$..%.%$;%%;-<-)++($.@.$...$@.@.@.$%%%%%%.&&,@&##@#@@@", +"++!$$$(+..*%*...@+_@_+[+[@@_{='=+[={!=@^$^@!{+_+++=++++#&#@(#.#..#*#.*.*.>..%$.-.%-;-%)-;3/0@@...@.$@.$@.@..%%%;%%%$&#&#@&@,@@#&", +"''])$@(+..%.%*..@'+'+[+=@[++=+=+='+=]=!]|]!@^++=++@++++@#.#,~.*#*..#*#.#..>.$%.%$;%-%;-<--@+@.$..$...@.@$&@%<%%%%%%&&&#&#@,@&@@@", +"/+@]$$+($.%*%%*.@@==/='=+]='+'='=+^='!{|=]|$@{++=+++},@#.@(&##..~**..#*.>.>.>.$%.-;%)%--;-)(]..@..@$.$.@.@:%;%4%%%$&#&&#.&#@@@+@", +"''!)$]+,..%..*..$++'+=+[+!@_!+_+='!+=@!1$_'[+=]$@=+++@@#.>,&&*#.#.~*#.*#.>.$.%$.-.-;-;-;-<$+&]..$..@.@:@.@.%%%%<%*.&&#&#@,&@,@@@", +"++{$$$@($.*%*%.>@'+=+'=+'='+'=+'=+^'^^@^@1+=+++@+@@++&#@.#(##.#*.*$*.#.*#*.*$..%.%%%-;-;-;-(@.$..@.$:$$.@...%%8%%.#.&&#&@@#@@@,@", +"_/!]$]@+.>%.%*..$++=++{=++=+=+=+{=+^+^]1+^@'/={++++2+#&#@#+&#.*.#.~.**#*.>.*..$..$;%%-<-%)-:$.@.$..$@.@:@...%.%%%.~#&#&@,@@@+@@@", +"+'+$]$+&&.*%*%.>$@+'+=+'={={+=+=+[+^='!^+^@|'++=++++@&#..#&#.#*.*.#*#.*.#*.>.%.%.%.-;%);-<--:@.@&$.$$].$&.....%%%.&#&@,&@,@#@@+@", +"_+{$$]@+$..%.*..$@}'++='+=+='='+!'_$!'={[@^@=++/++++#@##@2&#.#*#.*.*.~>~.*..>..$..%$%-%-)%-<@&@&@&@.@$@.$....4%%.#&,&@,@&@@+@@@+", +"+/=]$$&&&>%*.%..@@+=+'}=+{=++=+_=@!{!={=={!+'_{+=+++#&$*@(#.#.>.*#.#.*.#.#*...%.%.%.;-;-;-;-.@&+(@@@@@@$...&.%%%.@&@,&@@@@@&@@++", +"+'+]$)@+...*%*..@++'=+'='=+[+='+'_+^$|{!]='=++=+/++@#.##2&#.#.*#*.*.*#.**.*.>*.$.$%$%;-%<--<$6@&@&+({(@+&$..%%%..,+&@,@@,@@@@@++", +"+++$$).(..%*%.%.@=+++=++='={+=+==+=={|!^@=+^/=+/+++#@#&,(#.#.*#.*#.*#*.#.#*...*.%..%$%;-;-%--&@@@@@++++(+%%.%%%.@(@+@&@@@+@@@+++", +"{@{]$]$+&..*%*.&$@_'+'='+=+='=++='=+_{$^!{='=/+=++@#$#&2#.#.>.~*.*.*.#*#*.#.>.>..$%.%-%-;3;-%{&@&@@@@++(,%>.%<.&++,@+@@(@@@@@+@+", +"@@@$$;$&@%%.%%.&$+++=+=+='=@=+[/=+='=!_@!{_++[+++@#@~,(,.#.#.*.~*#.**..*.*.*...*..$%.;-;-%-;-$@&@@&@@++(+.%-.>&(+@+@@@@+@@@@@@++", +"+{@]$]$&&.*.*%%&@=+=+'='+='='=@='!+=+^@^+='=+/+++@@#(2,~#.#.#*#.*.~.#*~.#*#.#*.$*.$%$.%-;)%-<-6@@@@6@@+@&>%%$&+(+@+@@+@+@@@@{+@+", +"@@@$$.]&@.%.%...@++'=+=+=+=++={}+!'!'=@1+=++++}+@(,(#~..~#.*.**~*.**.*>.*.~.*.>..>.%.-;%-;-%--+@&@@@@&@&#.>.&,&+@+@@@@+@@&@@++++", +"+@{@)$$&..*%*%..{++=+{='='=^+!@='!+=]={!{='=++++,(#&&#~#.*##.#*.#.#*.*.~.#*.#.**...$%.$;%)%);-:@@@&@@@&@&..,@(@&+@@@@++@@@@@@+++", +"+'@@$)$&@..%%%.#++_+=+=+!+=@[+=@={!_+={!@++++/+,@&.#*.*~#..~.#****.~.#*.*..*.*#.>>.%$%%$%)%----+6@@@&@@.##&~@#@+@@@+@+@@]@@++{++", +"+++{$-)$..%*.%.@++++'+{='='@={=$=+!'^$_+^+_+++@$&#&~&~*.~#*#*.~.~.*.*.#*.#*#.*.*..>..$;%;-;;-;-+@&@@&$&#&&#&#&@@@@+@+@@.@@@+++++", +"++'@)-.$&$.%%%%&+'=+==+=+=={=@={@[@='={!$@+++,@,.&.#*&~~.*&.#*#*#*~.*.*.*..*>.*>.>.%.%$;%)%--;-@&@&@@.@.#&#.#&@@+@+@@@@@@@++++!@", +"++++-))$...>.%.@++_++{={'=@_+=+={=]+_@[/^@+@+@#@#.~.~*.~*#*#*.~.*.*.**.*.#*.*#.*.>.$..$;$%);--)$6@&@&#.@*&#.#@,@+@@+@@$@@@++++@@"}; diff --git a/MANIFEST.in b/MANIFEST.in index d0cf89c6b..3807bdd32 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,6 +8,10 @@ include *.rst include *.c include *.h include selftest.py tox.ini +recursive-include Tests *.py *.txt +graft Tests/fonts +graft Tests/icc +graft Tests/images recursive-include Scripts *.py README graft Images recursive-include docs *.txt *.html *.rst *.css *.py README CHANGES CONTENTS Makefile make.bat diff --git a/PIL/ArgImagePlugin.py b/PIL/ArgImagePlugin.py index 815fcea33..69b50acdc 100644 --- a/PIL/ArgImagePlugin.py +++ b/PIL/ArgImagePlugin.py @@ -18,13 +18,15 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + __version__ = "0.4" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette -from PngImagePlugin import i16, i32, ChunkStream, _MODES +from .PngImagePlugin import i8, i16, i32, ChunkStream, _MODES -MAGIC = "\212ARG\r\n\032\n" +MAGIC = b"\212ARG\r\n\032\n" # -------------------------------------------------------------------- # ARG parser @@ -60,18 +62,18 @@ class ArgStream(ChunkStream): # assertions if self.count != 0: - raise SyntaxError, "misplaced AHDR chunk" + raise SyntaxError("misplaced AHDR chunk") s = self.fp.read(bytes) self.size = i32(s), i32(s[4:]) try: - self.mode, self.rawmode = _MODES[(ord(s[8]), ord(s[9]))] + self.mode, self.rawmode = _MODES[(i8(s[8]), i8(s[9]))] except: - raise SyntaxError, "unknown ARG mode" + raise SyntaxError("unknown ARG mode") if Image.DEBUG: - print "AHDR size", self.size - print "AHDR mode", self.mode, self.rawmode + print("AHDR size", self.size) + print("AHDR mode", self.mode, self.rawmode) return s @@ -80,7 +82,7 @@ class ArgStream(ChunkStream): # assertions if self.count != 0: - raise SyntaxError, "misplaced AFRM chunk" + raise SyntaxError("misplaced AFRM chunk") self.show = 1 self.id = 0 @@ -98,7 +100,7 @@ class ArgStream(ChunkStream): self.repair = None if Image.DEBUG: - print "AFRM", self.id, self.count + print("AFRM", self.id, self.count) return s @@ -107,7 +109,7 @@ class ArgStream(ChunkStream): # assertions if self.count != 0: - raise SyntaxError, "misplaced ADEF chunk" + raise SyntaxError("misplaced ADEF chunk") self.show = 0 self.id = 0 @@ -121,7 +123,7 @@ class ArgStream(ChunkStream): self.count = i16(s[2:4]) if Image.DEBUG: - print "ADEF", self.id, self.count + print("ADEF", self.id, self.count) return s @@ -130,7 +132,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced NAME chunk" + raise SyntaxError("misplaced NAME chunk") name = self.fp.read(bytes) self.names[self.id] = name @@ -141,26 +143,26 @@ class ArgStream(ChunkStream): "AEND -- end of animation" if Image.DEBUG: - print "AEND" + print("AEND") self.eof = 1 - raise EOFError, "end of ARG file" + raise EOFError("end of ARG file") def __getmodesize(self, s, full=1): size = i32(s), i32(s[4:]) try: - mode, rawmode = _MODES[(ord(s[8]), ord(s[9]))] + mode, rawmode = _MODES[(i8(s[8]), i8(s[9]))] except: - raise SyntaxError, "unknown image mode" + raise SyntaxError("unknown image mode") if full: - if ord(s[12]): + if i8(s[12]): pass # interlace not yet supported - if ord(s[11]): - raise SyntaxError, "unknown filter category" + if i8(s[11]): + raise SyntaxError("unknown filter category") return size, mode, rawmode @@ -169,7 +171,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced PAST chunk" + raise SyntaxError("misplaced PAST chunk") if self.repair is not None: # we must repair the target image before we @@ -206,7 +208,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced BLNK chunk" + raise SyntaxError("misplaced BLNK chunk") s = self.fp.read(bytes) size, mode, rawmode = self.__getmodesize(s, 0) @@ -223,7 +225,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced IHDR chunk" + raise SyntaxError("misplaced IHDR chunk") # image header s = self.fp.read(bytes) @@ -234,7 +236,7 @@ class ArgStream(ChunkStream): self.im = Image.core.new(mode, size) self.decoder = Image.core.zip_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s @@ -243,20 +245,20 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced DHDR chunk" + raise SyntaxError("misplaced DHDR chunk") s = self.fp.read(bytes) size, mode, rawmode = self.__getmodesize(s) # delta header - diff = ord(s[13]) + diff = i8(s[13]) offs = i32(s[14:18]), i32(s[18:22]) bbox = offs + (offs[0]+size[0], offs[1]+size[1]) if Image.DEBUG: - print "DHDR", diff, bbox + print("DHDR", diff, bbox) # FIXME: decode and apply image self.action = ("DHDR", diff, bbox) @@ -267,7 +269,7 @@ class ArgStream(ChunkStream): self.decoder = Image.core.zip_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s @@ -276,7 +278,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced JHDR chunk" + raise SyntaxError("misplaced JHDR chunk") # image header s = self.fp.read(bytes) @@ -287,7 +289,7 @@ class ArgStream(ChunkStream): self.im = Image.core.new(mode, size) self.decoder = Image.core.jpeg_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s @@ -296,7 +298,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced UHDR chunk" + raise SyntaxError("misplaced UHDR chunk") # image header s = self.fp.read(bytes) @@ -307,7 +309,7 @@ class ArgStream(ChunkStream): self.im = Image.core.new(mode, size) self.decoder = Image.core.raw_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s @@ -321,7 +323,7 @@ class ArgStream(ChunkStream): if n < 0: # end of image if e < 0: - raise IOError, "decoder error %d" % e + raise IOError("decoder error %d" % e) else: self.data = self.data[n:] @@ -386,7 +388,7 @@ class ArgStream(ChunkStream): "SYNC -- reset decoder" if self.count != 0: - raise SyntaxError, "misplaced sYNC chunk" + raise SyntaxError("misplaced sYNC chunk") s = self.fp.read(bytes) self.__reset() @@ -418,7 +420,7 @@ class ArgImageFile(ImageFile.ImageFile): ) if self.fp.read(8) != MAGIC: - raise SyntaxError, "not an ARG file" + raise SyntaxError("not an ARG file") self.arg = ArgStream(self.fp) @@ -427,7 +429,7 @@ class ArgImageFile(ImageFile.ImageFile): cid, offset, bytes = self.arg.read() if cid != "AHDR": - raise SyntaxError, "expected an AHDR chunk" + raise SyntaxError("expected an AHDR chunk") s = self.arg.call(cid, offset, bytes) @@ -452,11 +454,11 @@ class ArgImageFile(ImageFile.ImageFile): def seek(self, frame): if self.arg.eof: - raise EOFError, "end of animation" + raise EOFError("end of animation") self.fp = self.arg.fp - while 1: + while True: # # process chunks @@ -464,7 +466,7 @@ class ArgImageFile(ImageFile.ImageFile): cid, offset, bytes = self.arg.read() if self.arg.eof: - raise EOFError, "end of animation" + raise EOFError("end of animation") try: s = self.arg.call(cid, offset, bytes) @@ -473,7 +475,7 @@ class ArgImageFile(ImageFile.ImageFile): except "glurk": # AttributeError if Image.DEBUG: - print cid, bytes, "(unknown)" + print(cid, bytes, "(unknown)") s = self.fp.read(bytes) self.arg.crc(cid, s) diff --git a/PIL/BdfFontFile.py b/PIL/BdfFontFile.py index 47b5f05c8..3899f90b6 100644 --- a/PIL/BdfFontFile.py +++ b/PIL/BdfFontFile.py @@ -17,10 +17,9 @@ # See the README file for information on usage and redistribution. # -import Image -import FontFile +from . import Image +from . import FontFile -import string # -------------------------------------------------------------------- # parse X Bitmap Distribution Format (BDF) @@ -44,39 +43,39 @@ bdf_spacing = { def bdf_char(f): # skip to STARTCHAR - while 1: + while True: s = f.readline() if not s: return None - if s[:9] == "STARTCHAR": + if s[:9] == b"STARTCHAR": break - id = string.strip(s[9:]) + id = s[9:].strip().decode('ascii') # load symbol properties props = {} - while 1: + while True: s = f.readline() - if not s or s[:6] == "BITMAP": + if not s or s[:6] == b"BITMAP": break - i = string.find(s, " ") - props[s[:i]] = s[i+1:-1] + i = s.find(b" ") + props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii') # load bitmap bitmap = [] - while 1: + while True: s = f.readline() - if not s or s[:7] == "ENDCHAR": + if not s or s[:7] == b"ENDCHAR": break bitmap.append(s[:-1]) - bitmap = string.join(bitmap, "") + bitmap = b"".join(bitmap) - [x, y, l, d] = map(int, string.split(props["BBX"])) - [dx, dy] = map(int, string.split(props["DWIDTH"])) + [x, y, l, d] = [int(s) for s in props["BBX"].split()] + [dx, dy] = [int(s) for s in props["DWIDTH"].split()] bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y) try: - im = Image.fromstring("1", (x, y), bitmap, "hex", "1") + im = Image.frombytes("1", (x, y), bitmap, "hex", "1") except ValueError: # deal with zero-width characters im = Image.new("1", (x, y)) @@ -93,38 +92,38 @@ class BdfFontFile(FontFile.FontFile): FontFile.FontFile.__init__(self) s = fp.readline() - if s[:13] != "STARTFONT 2.1": - raise SyntaxError, "not a valid BDF file" + if s[:13] != b"STARTFONT 2.1": + raise SyntaxError("not a valid BDF file") props = {} comments = [] - while 1: + while True: s = fp.readline() - if not s or s[:13] == "ENDPROPERTIES": + if not s or s[:13] == b"ENDPROPERTIES": break - i = string.find(s, " ") - props[s[:i]] = s[i+1:-1] - if s[:i] in ["COMMENT", "COPYRIGHT"]: - if string.find(s, "LogicalFontDescription") < 0: - comments.append(s[i+1:-1]) + i = s.find(b" ") + props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii') + if s[:i] in [b"COMMENT", b"COPYRIGHT"]: + if s.find(b"LogicalFontDescription") < 0: + comments.append(s[i+1:-1].decode('ascii')) - font = string.split(props["FONT"], "-") + font = props["FONT"].split("-") - font[4] = bdf_slant[string.upper(font[4])] - font[11] = bdf_spacing[string.upper(font[11])] + font[4] = bdf_slant[font[4].upper()] + font[11] = bdf_spacing[font[11].upper()] ascent = int(props["FONT_ASCENT"]) descent = int(props["FONT_DESCENT"]) - fontname = string.join(font[1:], ";") + fontname = ";".join(font[1:]) # print "#", fontname # for i in comments: # print "#", i font = [] - while 1: + while True: c = bdf_char(fp) if not c: break diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 09c0a2141..cbfd82ed8 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -27,21 +27,19 @@ __version__ = "0.7" -import string -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le +o8 = _binary.o8 +o16 = _binary.o16le +o32 = _binary.o32le # # -------------------------------------------------------------------- # Read BMP file -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) - - BIT2MODE = { # bits => mode, rawmode 1: ("P", "P;1"), @@ -53,7 +51,7 @@ BIT2MODE = { } def _accept(prefix): - return prefix[:2] == "BM" + return prefix[:2] == b"BM" ## # Image plugin for the Windows BMP format. @@ -93,7 +91,7 @@ class BmpImageFile(ImageFile.ImageFile): lutsize = 4 colors = i32(s[32:]) direction = -1 - if s[11] == '\xff': + if i8(s[11]) == 0xff: # upside-down storage self.size = self.size[0], 2**32 - self.size[1] direction = 0 @@ -132,10 +130,10 @@ class BmpImageFile(ImageFile.ImageFile): if colors == 2: indices = (0, 255) else: - indices = range(colors) + indices = list(range(colors)) for i in indices: rgb = read(lutsize)[:3] - if rgb != chr(i)*3: + if rgb != o8(i)*3: greyscale = 0 palette.append(rgb) if greyscale: @@ -146,7 +144,7 @@ class BmpImageFile(ImageFile.ImageFile): else: self.mode = "P" self.palette = ImagePalette.raw( - "BGR", string.join(palette, "") + "BGR", b"".join(palette) ) if not offset: @@ -163,7 +161,7 @@ class BmpImageFile(ImageFile.ImageFile): # HEAD s = self.fp.read(14) - if s[:2] != "BM": + if s[:2] != b"BM": raise SyntaxError("Not a BMP file") offset = i32(s[10:]) @@ -182,12 +180,6 @@ class DibImageFile(BmpImageFile): # -------------------------------------------------------------------- # Write BMP file -def o16(i): - return chr(i&255) + chr(i>>8&255) - -def o32(i): - return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) - SAVE = { "1": ("1", 1, 2), "L": ("L", 8, 256), @@ -205,13 +197,13 @@ def _save(im, fp, filename, check=0): if check: return check - stride = ((im.size[0]*bits+7)/8+3)&(~3) + stride = ((im.size[0]*bits+7)//8+3)&(~3) header = 40 # or 64 for OS/2 version 2 offset = 14 + header + colors * 4 image = stride * im.size[1] # bitmap header - fp.write("BM" + # file type (magic) + fp.write(b"BM" + # file type (magic) o32(offset+image) + # file size o32(0) + # reserved o32(offset)) # image data offset @@ -228,14 +220,14 @@ def _save(im, fp, filename, check=0): o32(colors) + # colors used o32(colors)) # colors important - fp.write("\000" * (header - 40)) # padding (for OS/2 format) + fp.write(b"\0" * (header - 40)) # padding (for OS/2 format) if im.mode == "1": for i in (0, 255): - fp.write(chr(i) * 4) + fp.write(o8(i) * 4) elif im.mode == "L": for i in range(256): - fp.write(chr(i) * 4) + fp.write(o8(i) * 4) elif im.mode == "P": fp.write(im.im.getpalette("RGB", "BGRX")) diff --git a/PIL/BufrStubImagePlugin.py b/PIL/BufrStubImagePlugin.py index 4b111b393..df716a238 100644 --- a/PIL/BufrStubImagePlugin.py +++ b/PIL/BufrStubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from . import Image, ImageFile _handler = None @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[:4] == "BUFR" or prefix[:4] == "ZCZC" + return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC" class BufrStubImageFile(ImageFile.StubImageFile): diff --git a/PIL/ContainerIO.py b/PIL/ContainerIO.py index dff50e6bf..f4a15b813 100644 --- a/PIL/ContainerIO.py +++ b/PIL/ContainerIO.py @@ -92,7 +92,7 @@ class ContainerIO: def readline(self): s = "" - while 1: + while True: c = self.read(1) if not c: break @@ -108,7 +108,7 @@ class ContainerIO: def readlines(self): l = [] - while 1: + while True: s = self.readline() if not s: break diff --git a/PIL/CurImagePlugin.py b/PIL/CurImagePlugin.py index 6a126e701..c35537dcb 100644 --- a/PIL/CurImagePlugin.py +++ b/PIL/CurImagePlugin.py @@ -19,21 +19,19 @@ __version__ = "0.1" -import Image, BmpImagePlugin +from . import Image, BmpImagePlugin, _binary # # -------------------------------------------------------------------- -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le def _accept(prefix): - return prefix[:4] == "\0\0\2\0" + return prefix[:4] == b"\0\0\2\0" ## # Image plugin for Windows Cursor files. @@ -50,20 +48,20 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): # check magic s = self.fp.read(6) if not _accept(s): - raise SyntaxError, "not an CUR file" + raise SyntaxError("not an CUR file") # pick the largest cursor in the file - m = "" + m = b"" for i in range(i16(s[4:])): s = self.fp.read(16) if not m: m = s - elif ord(s[0]) > ord(m[0]) and ord(s[1]) > ord(m[1]): + elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]): m = s - #print "width", ord(s[0]) - #print "height", ord(s[1]) - #print "colors", ord(s[2]) - #print "reserved", ord(s[3]) + #print "width", i8(s[0]) + #print "height", i8(s[1]) + #print "colors", i8(s[2]) + #print "reserved", i8(s[3]) #print "hotspot x", i16(s[4:]) #print "hotspot y", i16(s[6:]) #print "bytes", i32(s[8:]) @@ -73,7 +71,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): self._bitmap(i32(m[12:]) + offset) # patch up the bitmap height - self.size = self.size[0], self.size[1]/2 + self.size = self.size[0], self.size[1]//2 d, e, o, a = self.tile[0] self.tile[0] = d, (0,0)+self.size, o, a diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index 32da7d831..0a08b0bf8 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -23,14 +23,13 @@ __version__ = "0.2" -import Image +from . import Image, _binary -from PcxImagePlugin import PcxImageFile +from .PcxImagePlugin import PcxImageFile MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i32 = _binary.i32le def _accept(prefix): return i32(prefix) == MAGIC @@ -48,7 +47,7 @@ class DcxImageFile(PcxImageFile): # Header s = self.fp.read(4) if i32(s) != MAGIC: - raise SyntaxError, "not a DCX file" + raise SyntaxError("not a DCX file") # Component directory self._offset = [] diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 837447bca..9d30bc40f 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -20,17 +20,15 @@ __version__ = "0.5" -import re, string -import Image, ImageFile +import re +import io +from . import Image, ImageFile, _binary # # -------------------------------------------------------------------- -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) - -def o32(i): - return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) +i32 = _binary.i32le +o32 = _binary.o32le split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") @@ -55,7 +53,7 @@ def Ghostscript(tile, size, fp): "-sOutputFile=%s" % file,# output file "- >/dev/null 2>/dev/null"] - command = string.join(command) + command = " ".join(command) # push data through ghostscript try: @@ -93,30 +91,32 @@ class PSFile: def seek(self, offset, whence=0): self.char = None self.fp.seek(offset, whence) + def read(self, count): + return self.fp.read(count).decode('latin-1') def tell(self): pos = self.fp.tell() if self.char: pos = pos - 1 return pos def readline(self): - s = "" + s = b"" if self.char: c = self.char self.char = None else: c = self.fp.read(1) - while c not in "\r\n": + while c not in b"\r\n": s = s + c c = self.fp.read(1) - if c == "\r": + if c == b"\r": self.char = self.fp.read(1) - if self.char == "\n": + if self.char == b"\n": self.char = None - return s + "\n" + return s.decode('latin-1') + "\n" def _accept(prefix): - return prefix[:4] == "%!PS" or i32(prefix) == 0xC6D3D0C5L + return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5 ## # Image plugin for Encapsulated Postscript. This plugin supports only @@ -141,12 +141,12 @@ class EpsImageFile(ImageFile.ImageFile): offset = 0 fp.seek(0, 2) length = fp.tell() - elif i32(s) == 0xC6D3D0C5L: + elif i32(s) == 0xC6D3D0C5: offset = i32(s[4:]) length = i32(s[8:]) fp.seek(offset) else: - raise SyntaxError, "not an EPS file" + raise SyntaxError("not an EPS file") fp.seek(offset) @@ -163,7 +163,7 @@ class EpsImageFile(ImageFile.ImageFile): while s: if len(s) > 255: - raise SyntaxError, "not an EPS file" + raise SyntaxError("not an EPS file") if s[-2:] == '\r\n': s = s[:-2] @@ -172,8 +172,8 @@ class EpsImageFile(ImageFile.ImageFile): try: m = split.match(s) - except re.error, v: - raise SyntaxError, "not an EPS file" + except re.error as v: + raise SyntaxError("not an EPS file") if m: k, v = m.group(1, 2) @@ -183,7 +183,7 @@ class EpsImageFile(ImageFile.ImageFile): # Note: The DSC spec says that BoundingBox # fields should be integers, but some drivers # put floating point values there anyway. - box = map(int, map(float, string.split(v))) + box = [int(float(s)) for s in v.split()] self.size = box[2] - box[0], box[3] - box[1] self.tile = [("eps", (0,0) + self.size, offset, (length, box))] @@ -196,18 +196,19 @@ class EpsImageFile(ImageFile.ImageFile): if m: k = m.group(1) + if k == "EndComments": break if k[:8] == "PS-Adobe": self.info[k[:8]] = k[9:] else: self.info[k] = "" - elif s[0] == '%': + elif s[0:1] == '%': # handle non-DSC Postscript comments that some # tools mistakenly put in the Comments section pass else: - raise IOError, "bad EPS header" + raise IOError("bad EPS header") s = fp.readline() @@ -221,7 +222,7 @@ class EpsImageFile(ImageFile.ImageFile): while s[0] == "%": if len(s) > 255: - raise SyntaxError, "not an EPS file" + raise SyntaxError("not an EPS file") if s[-2:] == '\r\n': s = s[:-2] @@ -231,7 +232,7 @@ class EpsImageFile(ImageFile.ImageFile): if s[:11] == "%ImageData:": [x, y, bi, mo, z3, z4, en, id] =\ - string.split(s[11:], maxsplit=7) + s[11:].split(None, 7) x = int(x); y = int(y) @@ -261,7 +262,7 @@ class EpsImageFile(ImageFile.ImageFile): id = id[1:-1] # Scan forward to the actual image data - while 1: + while True: s = fp.readline() if not s: break @@ -278,7 +279,7 @@ class EpsImageFile(ImageFile.ImageFile): break if not box: - raise IOError, "cannot determine EPS bounding box" + raise IOError("cannot determine EPS bounding box") def load(self): # Load EPS via Ghostscript @@ -308,7 +309,18 @@ def _save(im, fp, filename, eps=1): elif im.mode == "CMYK": operator = (8, 4, "false 4 colorimage") else: - raise ValueError, "image mode is not supported" + raise ValueError("image mode is not supported") + + class NoCloseStream: + def __init__(self, fp): + self.fp = fp + def __getattr__(self, name): + return getattr(self.fp, name) + def close(self): + pass + + base_fp = fp + fp = io.TextIOWrapper(NoCloseStream(fp), encoding='latin-1') if eps: # @@ -332,9 +344,10 @@ def _save(im, fp, filename, eps=1): fp.write("%d %d 8\n" % im.size) # <= bits fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])) fp.write("{ currentfile buf readhexstring pop } bind\n") - fp.write("%s\n" % operator[2]) + fp.write(operator[2] + "\n") + fp.flush() - ImageFile._save(im, fp, [("eps", (0,0)+im.size, 0, None)]) + ImageFile._save(im, base_fp, [("eps", (0,0)+im.size, 0, None)]) fp.write("\n%%%%EndBinary\n") fp.write("grestore end\n") diff --git a/PIL/FitsStubImagePlugin.py b/PIL/FitsStubImagePlugin.py index ac8be80a9..6dee89a4b 100644 --- a/PIL/FitsStubImagePlugin.py +++ b/PIL/FitsStubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from . import Image, ImageFile _handler = None @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[:6] == "SIMPLE" + return prefix[:6] == b"SIMPLE" class FITSStubImageFile(ImageFile.StubImageFile): diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index dac8c435d..6af61c6e6 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -18,15 +18,12 @@ __version__ = "0.2" -import Image, ImageFile, ImagePalette -import string +from . import Image, ImageFile, ImagePalette, _binary - -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le +o8 = _binary.o8 # # decoder @@ -51,7 +48,7 @@ class FliImageFile(ImageFile.ImageFile): if not (magic in [0xAF11, 0xAF12] and i16(s[14:16]) in [0, 3] and # flags s[20:22] == '\x00\x00'): # reserved - raise SyntaxError, "not an FLI/FLC file" + raise SyntaxError("not an FLI/FLC file") # image characteristics self.mode = "P" @@ -64,7 +61,7 @@ class FliImageFile(ImageFile.ImageFile): self.info["duration"] = duration # look for palette - palette = map(lambda a: (a,a,a), range(256)) + palette = [(a,a,a) for a in range(256)] s = self.fp.read(16) @@ -83,8 +80,8 @@ class FliImageFile(ImageFile.ImageFile): elif i16(s[4:6]) == 4: self._palette(palette, 0) - palette = map(lambda (r,g,b): chr(r)+chr(g)+chr(b), palette) - self.palette = ImagePalette.raw("RGB", string.join(palette, "")) + palette = [o8(r)+o8(g)+o8(b) for (r,g,b) in palette] + self.palette = ImagePalette.raw("RGB", b"".join(palette)) # set things up to decode first frame self.frame = -1 @@ -98,22 +95,22 @@ class FliImageFile(ImageFile.ImageFile): i = 0 for e in range(i16(self.fp.read(2))): s = self.fp.read(2) - i = i + ord(s[0]) - n = ord(s[1]) + i = i + i8(s[0]) + n = i8(s[1]) if n == 0: n = 256 s = self.fp.read(n * 3) for n in range(0, len(s), 3): - r = ord(s[n]) << shift - g = ord(s[n+1]) << shift - b = ord(s[n+2]) << shift + r = i8(s[n]) << shift + g = i8(s[n+1]) << shift + b = i8(s[n+2]) << shift palette[i] = (r, g, b) i = i + 1 def seek(self, frame): if frame != self.frame + 1: - raise ValueError, "cannot seek to frame %d" % frame + raise ValueError("cannot seek to frame %d" % frame) self.frame = frame # move to next frame diff --git a/PIL/FontFile.py b/PIL/FontFile.py index d9d8eb38a..7e3905c92 100644 --- a/PIL/FontFile.py +++ b/PIL/FontFile.py @@ -15,7 +15,7 @@ # import os -import Image +from . import Image, _binary import marshal @@ -31,7 +31,7 @@ def puti16(fp, values): for v in values: if v < 0: v = v + 65536 - fp.write(chr(v>>8&255) + chr(v&255)) + fp.write(_binary.o16be(v)) ## # Base class for raster font file handlers. @@ -106,9 +106,9 @@ class FontFile: # font metrics fp = open(os.path.splitext(filename)[0] + ".pil", "wb") - fp.write("PILfont\n") - fp.write(";;;;;;%d;\n" % self.ysize) # HACK!!! - fp.write("DATA\n") + fp.write(b"PILfont\n") + fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!! + fp.write(b"DATA\n") for id in range(256): m = self.metrics[id] if not m: @@ -128,13 +128,13 @@ class FontFile: data = marshal.dumps((self.metrics, self.info)) if zlib: - data = "z" + zlib.compress(data, 9) + data = b"z" + zlib.compress(data, 9) else: - data = "u" + data + data = b"u" + data fp = open(os.path.splitext(filename)[0] + ".pil", "wb") - fp.write("PILfont2\n" + self.name + "\n" + "DATA\n") + fp.write(b"PILfont2\n" + self.name + "\n" + "DATA\n") fp.write(data) diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index b78fe8c70..7dc35b6e7 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -19,8 +19,8 @@ __version__ = "0.1" -import Image, ImageFile -from OleFileIO import * +from . import Image, ImageFile +from .OleFileIO import * # we map from colour field tuples to (mode, rawmode) descriptors @@ -60,10 +60,10 @@ class FpxImageFile(ImageFile.ImageFile): try: self.ole = OleFileIO(self.fp) except IOError: - raise SyntaxError, "not an FPX file; invalid OLE file" + raise SyntaxError("not an FPX file; invalid OLE file") if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": - raise SyntaxError, "not an FPX file; bad root CLSID" + raise SyntaxError("not an FPX file; bad root CLSID") self._open_index(1) @@ -108,7 +108,7 @@ class FpxImageFile(ImageFile.ImageFile): self.jpeg = {} for i in range(256): id = 0x3000001|(i << 16) - if prop.has_key(id): + if id in prop: self.jpeg[i] = prop[id] # print len(self.jpeg), "tables loaded" @@ -143,7 +143,7 @@ class FpxImageFile(ImageFile.ImageFile): # print size, self.mode, self.rawmode if size != self.size: - raise IOError, "subimage mismatch" + raise IOError("subimage mismatch") # get tile descriptors fp.seek(28 + offset) @@ -170,8 +170,8 @@ class FpxImageFile(ImageFile.ImageFile): elif compression == 2: - internal_color_conversion = ord(s[14]) - jpeg_tables = ord(s[15]) + internal_color_conversion = i8(s[14]) + jpeg_tables = i8(s[15]) rawmode = self.rawmode if internal_color_conversion: @@ -198,7 +198,7 @@ class FpxImageFile(ImageFile.ImageFile): self.tile_prefix = self.jpeg[jpeg_tables] else: - raise IOError, "unknown/invalid compression" + raise IOError("unknown/invalid compression") x = x + xtile if x >= xsize: diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index c71a0f77f..e7d611f00 100644 --- a/PIL/GbrImagePlugin.py +++ b/PIL/GbrImagePlugin.py @@ -13,10 +13,9 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from . import Image, ImageFile, _binary -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24L) +i32 = _binary.i32be def _accept(prefix): return i32(prefix) >= 20 and i32(prefix[4:8]) == 1 @@ -34,13 +33,13 @@ class GbrImageFile(ImageFile.ImageFile): header_size = i32(self.fp.read(4)) version = i32(self.fp.read(4)) if header_size < 20 or version != 1: - raise SyntaxError, "not a GIMP brush" + raise SyntaxError("not a GIMP brush") width = i32(self.fp.read(4)) height = i32(self.fp.read(4)) bytes = i32(self.fp.read(4)) if width <= 0 or height <= 0 or bytes != 1: - raise SyntaxError, "not a GIMP brush" + raise SyntaxError("not a GIMP brush") comment = self.fp.read(header_size - 20)[:-1] @@ -59,8 +58,8 @@ class GbrImageFile(ImageFile.ImageFile): # create an image out of the brush data block self.im = Image.core.new(self.mode, self.size) - self.im.fromstring(self.data) - self.data = "" + self.im.frombytes(self.data) + self.data = b"" # # registry diff --git a/PIL/GdImageFile.py b/PIL/GdImageFile.py index b5f1dd25d..01f30141f 100644 --- a/PIL/GdImageFile.py +++ b/PIL/GdImageFile.py @@ -25,10 +25,15 @@ __version__ = "0.1" -import ImageFile, ImagePalette +from . import ImageFile, ImagePalette, _binary -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +i16 = _binary.i16be ## # Image plugin for the GD uncompressed format. Note that this format @@ -72,10 +77,9 @@ def open(fp, mode = "r"): if mode != "r": raise ValueError("bad mode") - if type(fp) == type(""): - import __builtin__ + if isinstance(fp, str): filename = fp - fp = __builtin__.open(fp, "rb") + fp = builtins.open(fp, "rb") else: filename = "" diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 4d03493b5..97bd416e5 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -28,24 +28,23 @@ __version__ = "0.9" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary # -------------------------------------------------------------------- # Helpers -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def o16(i): - return chr(i&255) + chr(i>>8&255) +i8 = _binary.i8 +i16 = _binary.i16le +o8 = _binary.o8 +o16 = _binary.o16le # -------------------------------------------------------------------- # Identify/read GIF files def _accept(prefix): - return prefix[:6] in ["GIF87a", "GIF89a"] + return prefix[:6] in [b"GIF87a", b"GIF89a"] ## # Image plugin for GIF images. This plugin supports both GIF87 and @@ -60,16 +59,16 @@ class GifImageFile(ImageFile.ImageFile): def data(self): s = self.fp.read(1) - if s and ord(s): - return self.fp.read(ord(s)) + if s and i8(s): + return self.fp.read(i8(s)) return None def _open(self): # Screen s = self.fp.read(13) - if s[:6] not in ["GIF87a", "GIF89a"]: - raise SyntaxError, "not a GIF file" + if s[:6] not in [b"GIF87a", b"GIF89a"]: + raise SyntaxError("not a GIF file") self.info["version"] = s[:6] @@ -77,17 +76,17 @@ class GifImageFile(ImageFile.ImageFile): self.tile = [] - flags = ord(s[10]) + flags = i8(s[10]) bits = (flags & 7) + 1 if flags & 128: # get global palette - self.info["background"] = ord(s[11]) + self.info["background"] = i8(s[11]) # check if palette contains colour indices p = self.fp.read(3<= 3 and ord(block[0]) == 1: + if len(block) >= 3 and i8(block[0]) == 1: self.info["loop"] = i16(block[1:3]) while self.data(): pass - elif s == ",": + elif s == b",": # # local image # @@ -177,7 +176,7 @@ class GifImageFile(ImageFile.ImageFile): # extent x0, y0 = i16(s[0:]), i16(s[2:]) x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:]) - flags = ord(s[8]) + flags = i8(s[8]) interlace = (flags & 64) != 0 @@ -187,7 +186,7 @@ class GifImageFile(ImageFile.ImageFile): ImagePalette.raw("RGB", self.fp.read(3< 100: - raise SyntaxError, "bad palette file" + raise SyntaxError("bad palette file") - v = tuple(map(int, string.split(s)[:3])) + v = tuple(map(int, s.split()[:3])) if len(v) != 3: - raise ValueError, "bad palette entry" + raise ValueError("bad palette entry") if 0 <= i <= 255: - self.palette[i] = chr(v[0]) + chr(v[1]) + chr(v[2]) + self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2]) i = i + 1 - self.palette = string.join(self.palette, "") + self.palette = b"".join(self.palette) def getpalette(self): diff --git a/PIL/GribStubImagePlugin.py b/PIL/GribStubImagePlugin.py index e232f90db..af573bf5d 100644 --- a/PIL/GribStubImagePlugin.py +++ b/PIL/GribStubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from . import Image, ImageFile _handler = None @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[0:4] == "GRIB" and prefix[7] == chr(1) + return prefix[0:4] == b"GRIB" and prefix[7] == b'\x01' class GribStubImageFile(ImageFile.StubImageFile): diff --git a/PIL/Hdf5StubImagePlugin.py b/PIL/Hdf5StubImagePlugin.py index bc5e37433..0875b9866 100644 --- a/PIL/Hdf5StubImagePlugin.py +++ b/PIL/Hdf5StubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from . import Image, ImageFile _handler = None @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[:8] == "\x89HDF\r\n\x1a\n" + return prefix[:8] == b"\x89HDF\r\n\x1a\n" class HDF5StubImageFile(ImageFile.StubImageFile): diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index 4d68fa232..3478a9643 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -14,27 +14,31 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile -import string, struct +from . import Image, ImageFile, _binary +import struct + +i8 = _binary.i8 HEADERSIZE = 8 def nextheader(fobj): return struct.unpack('>4sI', fobj.read(HEADERSIZE)) -def read_32t(fobj, (start, length), (width, height)): +def read_32t(fobj, start_length, size): # The 128x128 icon seems to have an extra header for some reason. + (start, length) = start_length fobj.seek(start) sig = fobj.read(4) - if sig != '\x00\x00\x00\x00': - raise SyntaxError, 'Unknown signature, expecting 0x00000000' - return read_32(fobj, (start + 4, length - 4), (width, height)) + if sig != b'\x00\x00\x00\x00': + raise SyntaxError('Unknown signature, expecting 0x00000000') + return read_32(fobj, (start + 4, length - 4), size) -def read_32(fobj, (start, length), size): +def read_32(fobj, start_length, size): """ Read a 32bit RGB icon resource. Seems to be either uncompressed or an RLE packbits-like scheme. """ + (start, length) = start_length fobj.seek(start) sizesq = size[0] * size[1] if length == sizesq * 3: @@ -51,7 +55,7 @@ def read_32(fobj, (start, length), size): byte = fobj.read(1) if not byte: break - byte = ord(byte) + byte = i8(byte) if byte & 0x80: blocksize = byte - 125 byte = fobj.read(1) @@ -68,13 +72,14 @@ def read_32(fobj, (start, length), size): "Error reading channel [%r left]" % bytesleft ) band = Image.frombuffer( - "L", size, string.join(data, ""), "raw", "L", 0, 1 + "L", size, b"".join(data), "raw", "L", 0, 1 ) im.im.putband(band.im, band_ix) return {"RGB": im} -def read_mk(fobj, (start, length), size): +def read_mk(fobj, start_length, size): # Alpha masks seem to be uncompressed + (start, length) = start_length fobj.seek(start) band = Image.frombuffer( "L", size, fobj.read(size[0]*size[1]), "raw", "L", 0, 1 @@ -85,20 +90,20 @@ class IcnsFile: SIZES = { (128, 128): [ - ('it32', read_32t), - ('t8mk', read_mk), + (b'it32', read_32t), + (b't8mk', read_mk), ], (48, 48): [ - ('ih32', read_32), - ('h8mk', read_mk), + (b'ih32', read_32), + (b'h8mk', read_mk), ], (32, 32): [ - ('il32', read_32), - ('l8mk', read_mk), + (b'il32', read_32), + (b'l8mk', read_mk), ], (16, 16): [ - ('is32', read_32), - ('s8mk', read_mk), + (b'is32', read_32), + (b's8mk', read_mk), ], } @@ -111,7 +116,7 @@ class IcnsFile: self.fobj = fobj sig, filesize = nextheader(fobj) if sig != 'icns': - raise SyntaxError, 'not an icns file' + raise SyntaxError('not an icns file') i = HEADERSIZE while i < filesize: sig, blocksize = nextheader(fobj) @@ -125,7 +130,7 @@ class IcnsFile: sizes = [] for size, fmts in self.SIZES.items(): for (fmt, reader) in fmts: - if self.dct.has_key(fmt): + if fmt in self.dct: sizes.append(size) break return sizes @@ -133,7 +138,7 @@ class IcnsFile: def bestsize(self): sizes = self.itersizes() if not sizes: - raise SyntaxError, "No 32bit icon resources found" + raise SyntaxError("No 32bit icon resources found") return max(sizes) def dataforsize(self, size): @@ -201,7 +206,7 @@ class IcnsImageFile(ImageFile.ImageFile): self.load_end() -Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == 'icns') +Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns') Image.register_extension("ICNS", '.icns') if __name__ == '__main__': diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index 94846dc0c..c484a6774 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -19,21 +19,19 @@ __version__ = "0.1" -import Image, BmpImagePlugin +from . import Image, BmpImagePlugin, _binary # # -------------------------------------------------------------------- -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le def _accept(prefix): - return prefix[:4] == "\0\0\1\0" + return prefix[:4] == b"\0\0\1\0" ## # Image plugin for Windows Icon files. @@ -48,20 +46,20 @@ class IcoImageFile(BmpImagePlugin.BmpImageFile): # check magic s = self.fp.read(6) if not _accept(s): - raise SyntaxError, "not an ICO file" + raise SyntaxError("not an ICO file") # pick the largest icon in the file - m = "" + m = b"" for i in range(i16(s[4:])): s = self.fp.read(16) if not m: m = s - elif ord(s[0]) > ord(m[0]) and ord(s[1]) > ord(m[1]): + elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]): m = s - #print "width", ord(s[0]) - #print "height", ord(s[1]) - #print "colors", ord(s[2]) - #print "reserved", ord(s[3]) + #print "width", i8(s[0]) + #print "height", i8(s[1]) + #print "colors", i8(s[2]) + #print "reserved", i8(s[3]) #print "planes", i16(s[4:]) #print "bitcount", i16(s[6:]) #print "bytes", i32(s[8:]) @@ -71,7 +69,7 @@ class IcoImageFile(BmpImagePlugin.BmpImageFile): self._bitmap(i32(m[12:])) # patch up the bitmap height - self.size = self.size[0], self.size[1]/2 + self.size = self.size[0], self.size[1]//2 d, e, o, a = self.tile[0] self.tile[0] = d, (0,0)+self.size, o, a diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index a76dcf04e..84a59c779 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -28,8 +28,9 @@ __version__ = "0.7" -import re, string -import Image, ImageFile, ImagePalette +import re +from . import Image, ImageFile, ImagePalette +from ._binary import i8, o8 # -------------------------------------------------------------------- @@ -91,7 +92,7 @@ for i in range(2, 33): # -------------------------------------------------------------------- # Read IM directory -split = re.compile(r"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") +split = re.compile(br"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") def number(s): try: @@ -112,8 +113,8 @@ class ImImageFile(ImageFile.ImageFile): # Quick rejection: if there's not an LF among the first # 100 bytes, this is (probably) not a text header. - if not "\n" in self.fp.read(100): - raise SyntaxError, "not an IM file" + if not b"\n" in self.fp.read(100): + raise SyntaxError("not an IM file") self.fp.seek(0) n = 0 @@ -125,91 +126,96 @@ class ImImageFile(ImageFile.ImageFile): self.rawmode = "L" - while 1: + while True: s = self.fp.read(1) # Some versions of IFUNC uses \n\r instead of \r\n... - if s == "\r": + if s == b"\r": continue - if not s or s[0] == chr(0) or s[0] == chr(26): + if not s or s == b'\0' or s == b'\x1A': break # FIXME: this may read whole file if not a text file s = s + self.fp.readline() if len(s) > 100: - raise SyntaxError, "not an IM file" + raise SyntaxError("not an IM file") - if s[-2:] == '\r\n': + if s[-2:] == b'\r\n': s = s[:-2] - elif s[-1:] == '\n': + elif s[-1:] == b'\n': s = s[:-1] try: m = split.match(s) - except re.error, v: - raise SyntaxError, "not an IM file" + except re.error as v: + raise SyntaxError("not an IM file") if m: k, v = m.group(1,2) + # Don't know if this is the correct encoding, but a decent guess + # (I guess) + k = k.decode('latin-1', 'replace') + v = v.decode('latin-1', 'replace') + # Convert value as appropriate if k in [FRAMES, SCALE, SIZE]: - v = string.replace(v, "*", ",") - v = tuple(map(number, string.split(v, ","))) + v = v.replace("*", ",") + v = tuple(map(number, v.split(","))) if len(v) == 1: v = v[0] - elif k == MODE and OPEN.has_key(v): + elif k == MODE and v in OPEN: v, self.rawmode = OPEN[v] # Add to dictionary. Note that COMMENT tags are # combined into a list of strings. if k == COMMENT: - if self.info.has_key(k): + if k in self.info: self.info[k].append(v) else: self.info[k] = [v] else: self.info[k] = v - if TAGS.has_key(k): + if k in TAGS: n = n + 1 else: - raise SyntaxError, "Syntax error in IM header: " + s + raise SyntaxError("Syntax error in IM header: " + s.decode('ascii', 'replace')) if not n: - raise SyntaxError, "Not an IM file" + raise SyntaxError("Not an IM file") # Basic attributes self.size = self.info[SIZE] self.mode = self.info[MODE] # Skip forward to start of image data - while s and s[0] != chr(26): + while s and s[0:1] != b'\x1A': s = self.fp.read(1) if not s: - raise SyntaxError, "File truncated" + raise SyntaxError("File truncated") - if self.info.has_key(LUT): + if LUT in self.info: # convert lookup table to palette or lut attribute palette = self.fp.read(768) greyscale = 1 # greyscale palette linear = 1 # linear greyscale palette for i in range(256): if palette[i] == palette[i+256] == palette[i+512]: - if palette[i] != chr(i): + if i8(palette[i]) != i: linear = 0 else: greyscale = 0 if self.mode == "L" or self.mode == "LA": if greyscale: if not linear: - self.lut = map(ord, palette[:256]) + self.lut = [i8(c) for c in palette[:256]] else: if self.mode == "L": self.mode = self.rawmode = "P" @@ -218,7 +224,7 @@ class ImImageFile(ImageFile.ImageFile): self.palette = ImagePalette.raw("RGB;L", palette) elif self.mode == "RGB": if not greyscale or not linear: - self.lut = map(ord, palette) + self.lut = [i8(c) for c in palette] self.frame = 0 @@ -253,7 +259,7 @@ class ImImageFile(ImageFile.ImageFile): def seek(self, frame): if frame < 0 or frame >= self.info[FRAMES]: - raise EOFError, "seek outside sequence" + raise EOFError("seek outside sequence") if self.frame == frame: return @@ -265,7 +271,7 @@ class ImImageFile(ImageFile.ImageFile): else: bits = 8 * len(self.mode) - size = ((self.size[0] * bits + 7) / 8) * self.size[1] + size = ((self.size[0] * bits + 7) // 8) * self.size[1] offs = self.__offset + frame * size self.fp = self.__fp @@ -304,7 +310,7 @@ def _save(im, fp, filename, check=0): try: type, rawmode = SAVE[im.mode] except KeyError: - raise ValueError, "Cannot save %s images as IM" % im.mode + raise ValueError("Cannot save %s images as IM" % im.mode) try: frames = im.encoderinfo["frames"] @@ -314,14 +320,14 @@ def _save(im, fp, filename, check=0): if check: return check - fp.write("Image type: %s image\r\n" % type) + fp.write(("Image type: %s image\r\n" % type).encode('ascii')) if filename: - fp.write("Name: %s\r\n" % filename) - fp.write("Image size (x*y): %d*%d\r\n" % im.size) - fp.write("File size (no of images): %d\r\n" % frames) + fp.write(("Name: %s\r\n" % filename).encode('ascii')) + fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode('ascii')) + fp.write(("File size (no of images): %d\r\n" % frames).encode('ascii')) if im.mode == "P": - fp.write("Lut: 1\r\n") - fp.write("\000" * (511-fp.tell()) + "\032") + fp.write(b"Lut: 1\r\n") + fp.write(b"\000" * (511-fp.tell()) + b"\032") if im.mode == "P": fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, -1))]) diff --git a/PIL/Image.py b/PIL/Image.py index aa87fb472..b2b5d2493 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -24,6 +24,8 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + VERSION = "1.1.7" try: @@ -52,7 +54,7 @@ try: # them. Note that other modules should not refer to _imaging # directly; import Image and use the Image.core variable instead. import _imaging as core -except ImportError, v: +except ImportError as v: core = _imaging_not_installed() if str(v)[:20] == "Module use of python" and warnings: # The _imaging C module is present, but not compiled for @@ -64,31 +66,27 @@ except ImportError, v: RuntimeWarning ) -import ImageMode -import ImagePalette +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ -import os, string, sys +from . import ImageMode +from ._binary import i8, o8 + +import os, sys # type stuff -from types import IntType, StringType, TupleType +import collections +import numbers -try: - UnicodeStringType = type(unicode("")) - ## - # (Internal) Checks if an object is a string. If the current - # Python version supports Unicode, this checks for both 8-bit - # and Unicode strings. +if bytes is str: def isStringType(t): - return isinstance(t, StringType) or isinstance(t, UnicodeStringType) -except NameError: + return isinstance(t, basestring) +else: def isStringType(t): - return isinstance(t, StringType) - -## -# (Internal) Checks if an object is a tuple. - -def isTupleType(t): - return isinstance(t, TupleType) + return isinstance(t, str) ## # (Internal) Checks if an object is an image object. @@ -103,8 +101,6 @@ def isImageType(t): def isDirectory(f): return isStringType(f) and os.path.isdir(f) -from operator import isNumberType, isSequenceType - # # Debug level @@ -186,16 +182,7 @@ _MODEINFO = { } -try: - byteorder = sys.byteorder -except AttributeError: - import struct - if struct.unpack("h", "\0\1")[0] == 1: - byteorder = "big" - else: - byteorder = "little" - -if byteorder == 'little': +if sys.byteorder == 'little': _ENDIAN = '<' else: _ENDIAN = '>' @@ -223,8 +210,7 @@ def _conv_type_shape(im): return shape+(extra,), typ -MODES = _MODEINFO.keys() -MODES.sort() +MODES = sorted(_MODEINFO.keys()) # raw modes that may be memory mapped. NOTE: if you change this, you # may have to modify the stride calculation in map.c too! @@ -293,23 +279,23 @@ def preinit(): return try: - import BmpImagePlugin + from . import BmpImagePlugin except ImportError: pass try: - import GifImagePlugin + from . import GifImagePlugin except ImportError: pass try: - import JpegImagePlugin + from . import JpegImagePlugin except ImportError: pass try: - import PpmImagePlugin + from . import PpmImagePlugin except ImportError: pass try: - import PngImagePlugin + from . import PngImagePlugin except ImportError: pass # try: @@ -342,7 +328,7 @@ def init(): # only check directories (including current, if present in the path) for directory in filter(isDirectory, directories): fullpath = os.path.abspath(directory) - if visited.has_key(fullpath): + if fullpath in visited: continue for file in os.listdir(directory): if file[-14:] == "ImagePlugin.py": @@ -355,8 +341,8 @@ def init(): del sys.path[0] except ImportError: if DEBUG: - print "Image: failed to import", - print f, ":", sys.exc_value + print("Image: failed to import", end=' ') + print(f, ":", sys.exc_info()[1]) visited[fullpath] = None if OPEN or SAVE: @@ -364,21 +350,21 @@ def init(): return 1 # -------------------------------------------------------------------- -# Codec factories (used by tostring/fromstring and ImageFile.load) +# Codec factories (used by tobytes/frombytes and ImageFile.load) def _getdecoder(mode, decoder_name, args, extra=()): # tweak arguments if args is None: args = () - elif not isTupleType(args): + elif not isinstance(args, tuple): args = (args,) try: # get decoder decoder = getattr(core, decoder_name + "_decoder") - # print decoder, (mode,) + args + extra - return apply(decoder, (mode,) + args + extra) + # print(decoder, mode, args + extra) + return decoder(mode, *args + extra) except AttributeError: raise IOError("decoder %s not available" % decoder_name) @@ -387,14 +373,14 @@ def _getencoder(mode, encoder_name, args, extra=()): # tweak arguments if args is None: args = () - elif not isTupleType(args): + elif not isinstance(args, tuple): args = (args,) try: # get encoder encoder = getattr(core, encoder_name + "_encoder") - # print encoder, (mode,) + args + extra - return apply(encoder, (mode,) + args + extra) + # print(encoder, mode, args + extra) + return encoder(mode, *args + extra) except AttributeError: raise IOError("encoder %s not available" % encoder_name) @@ -402,26 +388,31 @@ def _getencoder(mode, encoder_name, args, extra=()): # -------------------------------------------------------------------- # Simple expression analyzer +def coerce_e(value): + return value if isinstance(value, _E) else _E(value) + class _E: - def __init__(self, data): self.data = data - def __coerce__(self, other): return self, _E(other) - def __add__(self, other): return _E((self.data, "__add__", other.data)) - def __mul__(self, other): return _E((self.data, "__mul__", other.data)) + def __init__(self, data): + self.data = data + def __add__(self, other): + return _E((self.data, "__add__", coerce_e(other).data)) + def __mul__(self, other): + return _E((self.data, "__mul__", coerce_e(other).data)) def _getscaleoffset(expr): stub = ["stub"] data = expr(_E(stub)).data try: (a, b, c) = data # simplified syntax - if (a is stub and b == "__mul__" and isNumberType(c)): + if (a is stub and b == "__mul__" and isinstance(c, numbers.Number)): return c, 0.0 - if (a is stub and b == "__add__" and isNumberType(c)): + if (a is stub and b == "__add__" and isinstance(c, numbers.Number)): return 1.0, c except TypeError: pass try: ((a, b, c), d, e) = data # full syntax - if (a is stub and b == "__mul__" and isNumberType(c) and - d == "__add__" and isNumberType(e)): + if (a is stub and b == "__mul__" and isinstance(c, numbers.Number) and + d == "__add__" and isinstance(e, numbers.Number)): return c, e except TypeError: pass raise ValueError("illegal expression") @@ -437,7 +428,7 @@ def _getscaleoffset(expr): # # @see #open # @see #new -# @see #fromstring +# @see #frombytes class Image: @@ -462,6 +453,7 @@ class Image: new.size = im.size new.palette = self.palette if im.mode == "P": + from . import ImagePalette new.palette = ImagePalette.ImagePalette() try: new.info = self.info.copy() @@ -505,7 +497,7 @@ class Image: shape, typestr = _conv_type_shape(self) new['shape'] = shape new['typestr'] = typestr - new['data'] = self.tostring() + new['data'] = self.tobytes() return new raise AttributeError(name) @@ -515,13 +507,13 @@ class Image: # @param encoder_name What encoder to use. The default is to # use the standard "raw" encoder. # @param *args Extra arguments to the encoder. - # @return An 8-bit string. + # @return A bytes object. - def tostring(self, encoder_name="raw", *args): - "Return image as a binary string" + def tobytes(self, encoder_name="raw", *args): + "Return image as a bytes object" # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] if encoder_name == "raw" and args == (): @@ -536,15 +528,21 @@ class Image: bufsize = max(65536, self.size[0] * 4) # see RawEncode.c data = [] - while 1: + while True: l, s, d = e.encode(bufsize) data.append(d) if s: break if s < 0: - raise RuntimeError("encoder error %d in tostring" % s) + raise RuntimeError("encoder error %d in tobytes" % s) - return string.join(data, "") + return b"".join(data) + + if bytes is str: + # Declare tostring as alias to tobytes + def tostring(self, *args, **kw): + warnings.warn('tostring() is deprecated. Please call tobytes() instead.', DeprecationWarning) + return self.tobytes(*args, **kw) ## # Returns the image converted to an X11 bitmap. This method @@ -560,23 +558,23 @@ class Image: self.load() if self.mode != "1": raise ValueError("not a bitmap") - data = self.tostring("xbm") - return string.join(["#define %s_width %d\n" % (name, self.size[0]), - "#define %s_height %d\n"% (name, self.size[1]), - "static char %s_bits[] = {\n" % name, data, "};"], "") + data = self.tobytes("xbm") + return b"".join([("#define %s_width %d\n" % (name, self.size[0])).encode('ascii'), + ("#define %s_height %d\n"% (name, self.size[1])).encode('ascii'), + ("static char %s_bits[] = {\n" % name).encode('ascii'), data, b"};"]) ## - # Loads this image with pixel data from a string. + # Loads this image with pixel data from a bytes object. #

- # This method is similar to the {@link #fromstring} function, but + # This method is similar to the {@link #frombytes} function, but # loads data into this image instead of creating a new image # object. - def fromstring(self, data, decoder_name="raw", *args): - "Load data to image from binary string" + def frombytes(self, data, decoder_name="raw", *args): + "Load data to image from a bytes object" # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] # default format @@ -593,6 +591,12 @@ class Image: if s[1] != 0: raise ValueError("cannot decode image data") + if bytes is str: + # Declare fromstring as alias to frombytes + def fromstring(self, *args, **kw): + warnings.warn('fromstring() is deprecated. Please call frombytes() instead.', DeprecationWarning) + return self.frombytes(*args, **kw) + ## # Allocates storage for the image and loads the pixel data. In # normal cases, you don't need to call this method, since the @@ -605,11 +609,11 @@ class Image: "Explicitly load pixel data." if self.im and self.palette and self.palette.dirty: # realize palette - apply(self.im.putpalette, self.palette.getdata()) + self.im.putpalette(*self.palette.getdata()) self.palette.dirty = 0 self.palette.mode = "RGB" self.palette.rawmode = None - if self.info.has_key("transparency"): + if "transparency" in self.info: self.im.putpalettealpha(self.info["transparency"], 0) self.palette.mode = "RGBA" if self.im: @@ -802,7 +806,7 @@ class Image: self.load() - if callable(filter): + if isinstance(filter, collections.Callable): filter = filter() if not hasattr(filter, "filter"): raise TypeError("filter argument should be ImageFilter.Filter instance or class") @@ -907,12 +911,12 @@ class Image: return self.im.getextrema() ## - # Returns a PyCObject that points to the internal image memory. + # Returns a capsule that points to the internal image memory. # - # @return A PyCObject object. + # @return A capsule object. def getim(self): - "Get PyCObject pointer to internal image memory" + "Get capsule pointer to internal image memory" self.load() return self.im.ptr @@ -929,7 +933,10 @@ class Image: self.load() try: - return map(ord, self.im.getpalette()) + if bytes is str: + return [i8(c) for c in self.im.getpalette()] + else: + return list(self.im.getpalette()) except ValueError: return None # no palette @@ -958,7 +965,7 @@ class Image: self.load() x, y = self.im.getprojection() - return map(ord, x), map(ord, y) + return [i8(c) for c in x], [i8(c) for c in y] ## # Returns a histogram for the image. The histogram is returned as @@ -1012,7 +1019,7 @@ class Image: "'offset' is deprecated; use 'ImageChops.offset' instead", DeprecationWarning, stacklevel=2 ) - import ImageChops + from . import ImageChops return ImageChops.offset(self, xoffset, yoffset) ## @@ -1079,7 +1086,7 @@ class Image: box = box + (box[0]+size[0], box[1]+size[1]) if isStringType(im): - import ImageColor + from . import ImageColor im = ImageColor.getcolor(im, self.mode) elif isImageType(im): @@ -1121,14 +1128,14 @@ class Image: if isinstance(lut, ImagePointHandler): return lut.point(self) - if not isSequenceType(lut): + if not isinstance(lut, collections.Sequence): # if it isn't a list, it should be a function if self.mode in ("I", "I;16", "F"): # check if the function can be used with point_transform scale, offset = _getscaleoffset(lut) return self._new(self.im.point_transform(scale, offset)) # for other modes, convert the function to a table - lut = map(lut, range(256)) * self.im.bands + lut = [lut(i) for i in range(256)] * self.im.bands if self.mode == "F": # FIXME: _imaging returns a confusing error message for this case @@ -1225,6 +1232,7 @@ class Image: def putpalette(self, data, rawmode="RGB"): "Put palette data into an image." + from . import ImagePalette if self.mode not in ("L", "P"): raise ValueError("illegal image mode") @@ -1232,8 +1240,11 @@ class Image: if isinstance(data, ImagePalette.ImagePalette): palette = ImagePalette.raw(data.rawmode, data.palette) else: - if not isStringType(data): - data = string.join(map(chr, data), "") + if not isinstance(data, bytes): + if bytes is str: + data = "".join(chr(x) for x in data) + else: + data = bytes(data) palette = ImagePalette.raw(rawmode, data) self.mode = "P" self.palette = palette @@ -1330,7 +1341,8 @@ class Image: math.cos(angle), math.sin(angle), 0.0, -math.sin(angle), math.cos(angle), 0.0 ] - def transform(x, y, (a, b, c, d, e, f)=matrix): + def transform(x, y, matrix=matrix): + (a, b, c, d, e, f) = matrix return a*x + b*y + c, d*x + e*y + f # calculate output size @@ -1408,7 +1420,7 @@ class Image: preinit() - ext = string.lower(os.path.splitext(filename)[1]) + ext = os.path.splitext(filename)[1].lower() if not format: try: @@ -1421,14 +1433,13 @@ class Image: raise KeyError(ext) # unknown extension try: - save_handler = SAVE[string.upper(format)] + save_handler = SAVE[format.upper()] except KeyError: init() - save_handler = SAVE[string.upper(format)] # unknown format + save_handler = SAVE[format.upper()] # unknown format if isStringType(fp): - import __builtin__ - fp = __builtin__.open(fp, "wb") + fp = builtins.open(fp, "wb") close = 1 else: close = 0 @@ -1755,13 +1766,13 @@ def new(mode, size, color=0): if isStringType(color): # css3-style specifier - import ImageColor + from . import ImageColor color = ImageColor.getcolor(color, mode) return Image()._new(core.fill(mode, size, color)) ## -# Creates an image memory from pixel data in a string. +# Creates a copy of an image memory from pixel data in a buffer. #

# In its simplest form, this function takes three arguments # (mode, size, and unpacked pixel data). @@ -1772,34 +1783,40 @@ def new(mode, size, color=0): #

# Note that this function decodes pixel data only, not entire images. # If you have an entire image in a string, wrap it in a -# StringIO object, and use {@link #open} to load it. +# BytesIO object, and use {@link #open} to load it. # # @param mode The image mode. # @param size The image size. -# @param data An 8-bit string containing raw data for the given mode. +# @param data A byte buffer containing raw data for the given mode. # @param decoder_name What decoder to use. # @param *args Additional parameters for the given decoder. # @return An Image object. -def fromstring(mode, size, data, decoder_name="raw", *args): - "Load image from string" +def frombytes(mode, size, data, decoder_name="raw", *args): + "Load image from byte buffer" # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] if decoder_name == "raw" and args == (): args = mode im = new(mode, size) - im.fromstring(data, decoder_name, args) + im.frombytes(data, decoder_name, args) return im +if bytes is str: + # Declare fromstring as an alias for frombytes + def fromstring(*args, **kw): + warnings.warn('fromstring() is deprecated. Please call frombytes() instead.', DeprecationWarning) + return frombytes(*args, **kw) + ## -# (New in 1.1.4) Creates an image memory from pixel data in a string -# or byte buffer. +# (New in 1.1.4) Creates an image memory referencing pixel data in a +# byte buffer. #

-# This function is similar to {@link #fromstring}, but uses data in +# This function is similar to {@link #frombytes}, but uses data in # the byte buffer, where possible. This means that changes to the # original buffer object are reflected in this image). Not all modes # can share memory; supported modes include "L", "RGBX", "RGBA", and @@ -1807,7 +1824,7 @@ def fromstring(mode, size, data, decoder_name="raw", *args): #

# Note that this function decodes pixel data only, not entire images. # If you have an entire image file in a string, wrap it in a -# StringIO object, and use {@link #open} to load it. +# BytesIO object, and use {@link #open} to load it. #

# In the current version, the default parameters used for the "raw" # decoder differs from that used for {@link fromstring}. This is a @@ -1818,7 +1835,7 @@ def fromstring(mode, size, data, decoder_name="raw", *args): # # @param mode The image mode. # @param size The image size. -# @param data An 8-bit string or other buffer object containing raw +# @param data A bytes or other buffer object containing raw # data for the given mode. # @param decoder_name What decoder to use. # @param *args Additional parameters for the given decoder. For the @@ -1829,10 +1846,10 @@ def fromstring(mode, size, data, decoder_name="raw", *args): # @since 1.1.4 def frombuffer(mode, size, data, decoder_name="raw", *args): - "Load image from string or buffer" + "Load image from bytes or buffer" # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] if decoder_name == "raw": @@ -1853,14 +1870,14 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): im.readonly = 1 return im - return fromstring(mode, size, data, decoder_name, args) + return frombytes(mode, size, data, decoder_name, args) ## # (New in 1.1.6) Creates an image memory from an object exporting # the array interface (using the buffer protocol). # -# If obj is not contiguous, then the tostring method is called +# If obj is not contiguous, then the tobytes method is called # and {@link frombuffer} is used. # # @param obj Object with array interface @@ -1895,7 +1912,7 @@ def fromarray(obj, mode=None): size = shape[1], shape[0] if strides is not None: - obj = obj.tostring() + obj = obj.tobytes() return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) @@ -1945,9 +1962,8 @@ def open(fp, mode="r"): raise ValueError("bad mode") if isStringType(fp): - import __builtin__ filename = fp - fp = __builtin__.open(fp, "rb") + fp = builtins.open(fp, "rb") else: filename = "" @@ -1962,6 +1978,8 @@ def open(fp, mode="r"): fp.seek(0) return factory(fp, filename) except (SyntaxError, IndexError, TypeError): + #import traceback + #traceback.print_exc() pass if init(): @@ -1973,6 +1991,8 @@ def open(fp, mode="r"): fp.seek(0) return factory(fp, filename) except (SyntaxError, IndexError, TypeError): + #import traceback + #traceback.print_exc() pass raise IOError("cannot identify image file") @@ -2093,7 +2113,7 @@ def merge(mode, bands): # reject images having another format. def register_open(id, factory, accept=None): - id = string.upper(id) + id = id.upper() ID.append(id) OPEN[id] = factory, accept @@ -2105,7 +2125,7 @@ def register_open(id, factory, accept=None): # @param mimetype The image MIME type for this format. def register_mime(id, mimetype): - MIME[string.upper(id)] = mimetype + MIME[id.upper()] = mimetype ## # Registers an image save function. This function should not be @@ -2115,7 +2135,7 @@ def register_mime(id, mimetype): # @param driver A function to save images in this format. def register_save(id, driver): - SAVE[string.upper(id)] = driver + SAVE[id.upper()] = driver ## # Registers an image extension. This function should not be @@ -2125,7 +2145,7 @@ def register_save(id, driver): # @param extension An extension used for this format. def register_extension(id, extension): - EXTENSION[string.lower(extension)] = string.upper(id) + EXTENSION[extension.lower()] = id.upper() # -------------------------------------------------------------------- @@ -2133,8 +2153,8 @@ def register_extension(id, extension): def _show(image, **options): # override me, as necessary - apply(_showxv, (image,), options) + _showxv(image, **options) def _showxv(image, title=None, **options): - import ImageShow - apply(ImageShow.show, (image, title), options) + from . import ImageShow + ImageShow.show(image, title, **options) diff --git a/PIL/ImageChops.py b/PIL/ImageChops.py index 82861fc7a..d37019575 100644 --- a/PIL/ImageChops.py +++ b/PIL/ImageChops.py @@ -15,7 +15,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image ## # The ImageChops module contains a number of arithmetical image diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index b8a6dca71..7f2d5f675 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -15,6 +15,8 @@ # below for the original description. # +from __future__ import print_function + DESCRIPTION = """ pyCMS @@ -79,7 +81,7 @@ VERSION = "0.1.0 pil" # --------------------------------------------------------------------. -import Image +from . import Image import _imagingcms core = _imagingcms @@ -122,7 +124,7 @@ FLAGS = { _MAX_FLAG = 0 for flag in FLAGS.values(): - if isinstance(flag, type(0)): + if isinstance(flag, int): _MAX_FLAG = _MAX_FLAG | flag # --------------------------------------------------------------------. @@ -140,7 +142,7 @@ class ImageCmsProfile: if Image.isStringType(profile): self._set(core.profile_open(profile), profile) elif hasattr(profile, "read"): - self._set(core.profile_fromstring(profile.read())) + self._set(core.profile_frombytes(profile.read())) else: self._set(profile) # assume it's already a profile @@ -205,7 +207,7 @@ class ImageCmsTransform(Image.ImagePointHandler): def get_display_profile(handle=None): import sys if sys.platform == "win32": - import ImageWin + from . import ImageWin if isinstance(handle, ImageWin.HDC): profile = core.get_display_profile_win32(handle, 1) else: @@ -288,10 +290,10 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER if outputMode is None: outputMode = im.mode - if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") - if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) try: @@ -307,7 +309,7 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER imOut = None else: imOut = transform.apply(im) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) return imOut @@ -334,7 +336,7 @@ def getOpenProfile(profileFilename): try: return ImageCmsProfile(profileFilename) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -396,10 +398,10 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent """ - if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") - if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) try: @@ -408,7 +410,7 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent if not isinstance(outputProfile, ImageCmsProfile): outputProfile = ImageCmsProfile(outputProfile) return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -487,10 +489,10 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo """ - if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") - if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) try: @@ -501,7 +503,7 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo if not isinstance(proofProfile, ImageCmsProfile): proofProfile = ImageCmsProfile(proofProfile) return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, proofProfile, proofRenderingIntent, flags) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) buildTransformFromOpenProfiles = buildTransform @@ -557,7 +559,7 @@ def applyTransform(im, transform, inPlace=0): imOut = None else: imOut = transform.apply(im) - except (TypeError, ValueError), v: + except (TypeError, ValueError) as v: raise PyCMSError(v) return imOut @@ -595,14 +597,14 @@ def createProfile(colorSpace, colorTemp=-1): raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace) if colorSpace == "LAB": - if type(colorTemp) == type(5000.0): + if isinstance(colorTemp, float): colorTemp = int(colorTemp + 0.5) - if type (colorTemp) != type (5000): + if not isinstance(colorTemp, int): raise PyCMSError("Color temperature must be a positive integer, \"%s\" not valid" % colorTemp) try: return core.createProfile(colorSpace, colorTemp) - except (TypeError, ValueError), v: + except (TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -633,7 +635,7 @@ def getProfileName(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) return profile.profile.product_name + "\n" - except (AttributeError, IOError, TypeError, ValueError), v: + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -665,7 +667,7 @@ def getProfileInfo(profile): profile = ImageCmsProfile(profile) # add an extra newline to preserve pyCMS compatibility return profile.product_info + "\n" - except (AttributeError, IOError, TypeError, ValueError), v: + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -703,7 +705,7 @@ def getDefaultIntent(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) return profile.profile.rendering_intent - except (AttributeError, IOError, TypeError, ValueError), v: + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -752,7 +754,7 @@ def isIntentSupported(profile, intent, direction): return 1 else: return -1 - except (AttributeError, IOError, TypeError, ValueError), v: + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -769,18 +771,17 @@ def versions(): if __name__ == "__main__": # create a cheap manual from the __doc__ strings for the functions above - import ImageCms - import string - print __doc__ + from . import ImageCms + print(__doc__) for f in dir(pyCMS): - print "="*80 - print "%s" %f + print("="*80) + print("%s" %f) try: exec ("doc = ImageCms.%s.__doc__" %(f)) - if string.find(doc, "pyCMS") >= 0: + if "pyCMS" in doc: # so we don't get the __doc__ string for imported modules - print doc + print(doc) except AttributeError: pass diff --git a/PIL/ImageColor.py b/PIL/ImageColor.py index c3cca46db..f1be7092c 100644 --- a/PIL/ImageColor.py +++ b/PIL/ImageColor.py @@ -17,16 +17,9 @@ # See the README file for information on usage and redistribution. # -import Image -import re, string +from . import Image +import re -try: - x = int("a", 16) -except TypeError: - # python 1.5.2 doesn't support int(x,b) - str2int = string.atoi -else: - str2int = int ## # Convert color string to RGB tuple. @@ -43,12 +36,12 @@ def getrgb(color): except KeyError: try: # fall back on case-insensitive lookup - rgb = colormap[string.lower(color)] + rgb = colormap[color.lower()] except KeyError: rgb = None # found color in cache if rgb: - if isinstance(rgb, type(())): + if isinstance(rgb, tuple): return rgb colormap[color] = rgb = getrgb(rgb) return rgb @@ -56,30 +49,30 @@ def getrgb(color): m = re.match("#\w\w\w$", color) if m: return ( - str2int(color[1]*2, 16), - str2int(color[2]*2, 16), - str2int(color[3]*2, 16) + int(color[1]*2, 16), + int(color[2]*2, 16), + int(color[3]*2, 16) ) m = re.match("#\w\w\w\w\w\w$", color) if m: return ( - str2int(color[1:3], 16), - str2int(color[3:5], 16), - str2int(color[5:7], 16) + int(color[1:3], 16), + int(color[3:5], 16), + int(color[5:7], 16) ) m = re.match("rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) if m: return ( - str2int(m.group(1)), - str2int(m.group(2)), - str2int(m.group(3)) + int(m.group(1)), + int(m.group(2)), + int(m.group(3)) ) m = re.match("rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) if m: return ( - int((str2int(m.group(1)) * 255) / 100.0 + 0.5), - int((str2int(m.group(2)) * 255) / 100.0 + 0.5), - int((str2int(m.group(3)) * 255) / 100.0 + 0.5) + int((int(m.group(1)) * 255) / 100.0 + 0.5), + int((int(m.group(2)) * 255) / 100.0 + 0.5), + int((int(m.group(3)) * 255) / 100.0 + 0.5) ) m = re.match("hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) if m: @@ -106,7 +99,7 @@ def getcolor(color, mode): return r, g, b, 255 if Image.getmodebase(mode) == "L": r, g, b = color - return (r*299 + g*587 + b*114)/1000 + return (r*299 + g*587 + b*114)//1000 return color colormap = { diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 5217a7366..d0e735abe 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -30,7 +30,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageColor +from . import Image, ImageColor try: import warnings @@ -127,7 +127,7 @@ class ImageDraw: def getfont(self): if not self.font: # FIXME: should add a font repository - import ImageFont + from . import ImageFont self.font = ImageFont.load_default() return self.font @@ -318,7 +318,7 @@ def getdraw(im=None, hints=None): except ImportError: pass if handler is None: - import ImageDraw2 + from . import ImageDraw2 handler = ImageDraw2 if im: im = handler.Draw(im) diff --git a/PIL/ImageDraw2.py b/PIL/ImageDraw2.py index dbf1a1f88..b453e81d5 100644 --- a/PIL/ImageDraw2.py +++ b/PIL/ImageDraw2.py @@ -16,7 +16,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageColor, ImageDraw, ImageFont, ImagePath +from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath class Pen: def __init__(self, color, width=1, opacity=255): @@ -68,7 +68,8 @@ class Draw: else: getattr(self.draw, op)(xy, fill=fill, outline=outline) - def settransform(self, (xoffset, yoffset)): + def settransform(self, offset): + (xoffset, yoffset) = offset self.transform = (1, 0, xoffset, 0, 1, yoffset) def arc(self, xy, start, end, *options): diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py index 86a19e650..767c5ba05 100644 --- a/PIL/ImageEnhance.py +++ b/PIL/ImageEnhance.py @@ -18,7 +18,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFilter, ImageStat +from . import Image, ImageFilter, ImageStat class _Enhance: diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 8a97c1b5b..eb4caef24 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -27,8 +27,9 @@ # See the README file for information on usage and redistribution. # -import Image -import traceback, string, os +from . import Image +import traceback, os +import io MAXBLOCK = 65536 @@ -55,9 +56,9 @@ def raise_ioerror(error): # -------------------------------------------------------------------- # Helpers -def _tilesort(t1, t2): +def _tilesort(t): # sort on offset - return cmp(t1[2], t2[2]) + return t[2] # # -------------------------------------------------------------------- @@ -89,25 +90,25 @@ class ImageFile(Image.Image): try: self._open() - except IndexError, v: # end of data + except IndexError as v: # end of data if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v - except TypeError, v: # end of data (ord) + raise SyntaxError(v) + except TypeError as v: # end of data (ord) if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v - except KeyError, v: # unsupported mode + raise SyntaxError(v) + except KeyError as v: # unsupported mode if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v - except EOFError, v: # got header but not the first frame + raise SyntaxError(v) + except EOFError as v: # got header but not the first frame if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v + raise SyntaxError(v) if not self.mode or self.size[0] <= 0: - raise SyntaxError, "not identified by this driver" + raise SyntaxError("not identified by this driver") def draft(self, mode, size): "Set draft mode" @@ -177,13 +178,13 @@ class ImageFile(Image.Image): if not self.map: # sort tiles in file order - self.tile.sort(_tilesort) + self.tile.sort(key=_tilesort) try: # FIXME: This is a hack to handle TIFF's JpegTables tag. prefix = self.tile_prefix except AttributeError: - prefix = "" + prefix = b"" for d, e, o, a in self.tile: d = Image._getdecoder(self.mode, d, a, self.decoderconfig) @@ -194,7 +195,7 @@ class ImageFile(Image.Image): continue b = prefix t = len(b) - while 1: + while True: s = read(self.decodermaxblock) if not s: self.tile = [] @@ -277,52 +278,6 @@ class StubImageFile(ImageFile): "StubImageFile subclass must implement _load" ) -## -# (Internal) Support class for the Parser file. - -class _ParserFile: - # parser support class. - - def __init__(self, data): - self.data = data - self.offset = 0 - - def close(self): - self.data = self.offset = None - - def tell(self): - return self.offset - - def seek(self, offset, whence=0): - if whence == 0: - self.offset = offset - elif whence == 1: - self.offset = self.offset + offset - else: - # force error in Image.open - raise IOError("illegal argument to seek") - - def read(self, bytes=0): - pos = self.offset - if bytes: - data = self.data[pos:pos+bytes] - else: - data = self.data[pos:] - self.offset = pos + len(data) - return data - - def readline(self): - # FIXME: this is slow! - s = "" - while 1: - c = self.read(1) - if not c: - break - s = s + c - if c == "\n": - break - return s - ## # Incremental image parser. This class implements the standard # feed/close consumer interface. @@ -398,11 +353,12 @@ class Parser: # attempt to open this file try: try: - fp = _ParserFile(self.data) + fp = io.BytesIO(self.data) im = Image.open(fp) finally: fp.close() # explicitly close the virtual file except IOError: + # traceback.print_exc() pass # not enough data else: flag = hasattr(im, "load_seek") or hasattr(im, "load_read") @@ -437,7 +393,7 @@ class Parser: # finish decoding if self.decoder: # get rid of what's left in the buffers - self.feed("") + self.feed(b"") self.data = self.decoder = None if not self.finished: raise IOError("image was incomplete") @@ -447,7 +403,7 @@ class Parser: # incremental parsing not possible; reopen the file # not that we have all data try: - fp = _ParserFile(self.data) + fp = io.BytesIO(self.data) self.image = Image.open(fp) finally: self.image.load() @@ -469,20 +425,20 @@ def _save(im, fp, tile): im.load() if not hasattr(im, "encoderconfig"): im.encoderconfig = () - tile.sort(_tilesort) + tile.sort(key=_tilesort) # FIXME: make MAXBLOCK a configuration parameter bufsize = max(MAXBLOCK, im.size[0] * 4) # see RawEncode.c try: fh = fp.fileno() fp.flush() - except AttributeError: + except (AttributeError, io.UnsupportedOperation): # compress to Python file-compatible object for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) if o > 0: fp.seek(o, 0) e.setimage(im.im, b) - while 1: + while True: l, s, d = e.encode(bufsize) fp.write(d) if s: @@ -515,7 +471,7 @@ def _save(im, fp, tile): def _safe_read(fp, size): if size <= 0: - return "" + return b"" if size <= SAFEBLOCK: return fp.read(size) data = [] @@ -525,4 +481,4 @@ def _safe_read(fp, size): break data.append(block) size = size - len(block) - return string.join(data, "") + return b"".join(data) diff --git a/PIL/ImageFileIO.py b/PIL/ImageFileIO.py index c12a25738..e2f53e38a 100644 --- a/PIL/ImageFileIO.py +++ b/PIL/ImageFileIO.py @@ -12,7 +12,7 @@ # See the README file for information on usage and redistribution. # -from StringIO import StringIO +from io import BytesIO ## # The ImageFileIO module can be used to read an image from a @@ -23,7 +23,7 @@ from StringIO import StringIO # # @see ImageFile#Parser -class ImageFileIO(StringIO): +class ImageFileIO(BytesIO): ## # Adds buffering to a stream file object, in order to @@ -36,4 +36,4 @@ class ImageFileIO(StringIO): def __init__(self, fp): data = fp.read() - StringIO.__init__(self, data) + BytesIO.__init__(self, data) diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py index 6d328cc1a..71f42c3ac 100644 --- a/PIL/ImageFilter.py +++ b/PIL/ImageFilter.py @@ -15,6 +15,8 @@ # See the README file for information on usage and redistribution. # +from functools import reduce + class Filter: pass @@ -52,7 +54,7 @@ class Kernel(Filter): def filter(self, image): if image.mode == "P": raise ValueError("cannot filter palette images") - return apply(image.filter, self.filterargs) + return image.filter(*self.filterargs) class BuiltinFilter(Kernel): def __init__(self): @@ -80,7 +82,7 @@ class RankFilter(Filter): def filter(self, image): if image.mode == "P": raise ValueError("cannot filter palette images") - image = image.expand(self.size/2, self.size/2) + image = image.expand(self.size//2, self.size//2) return image.rankfilter(self.size, self.rank) ## @@ -97,7 +99,7 @@ class MedianFilter(RankFilter): def __init__(self, size=3): self.size = size - self.rank = size*size/2 + self.rank = size*size//2 ## # Min filter. Picks the lowest pixel value in a window with the given diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 3ea2f2f8e..9a442b6dd 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -25,8 +25,10 @@ # See the README file for information on usage and redistribution. # -import Image -import os, string, sys +from __future__ import print_function + +from . import Image +import os, sys class _imagingft_not_installed: # module placeholder @@ -97,13 +99,13 @@ class ImageFont: def _load_pilfont_data(self, file, image): # read PILfont header - if file.readline() != "PILfont\n": + if file.readline() != b"PILfont\n": raise SyntaxError("Not a PILfont file") - d = string.split(file.readline(), ";") + d = file.readline().split(b";") self.info = [] # FIXME: should be a dictionary while True: s = file.readline() - if not s or s == "DATA\n": + if not s or s == b"DATA\n": break self.info.append(s) @@ -253,12 +255,12 @@ def load_path(filename): def load_default(): "Load a default font." - from StringIO import StringIO + from io import BytesIO import base64 f = ImageFont() f._load_pilfont_data( # courB08 - StringIO(base64.decodestring(''' + BytesIO(base64.decodestring(b''' UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -350,7 +352,7 @@ AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// +QAGAAIAzgAKANUAEw== -''')), Image.open(StringIO(base64.decodestring(''' +''')), Image.open(BytesIO(base64.decodestring(b''' iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g @@ -381,10 +383,10 @@ if __name__ == "__main__": # create font data chunk for embedding import base64, os, sys font = "../Images/courB08" - print " f._load_pilfont_data(" - print " # %s" % os.path.basename(font) - print " StringIO(base64.decodestring('''" + print(" f._load_pilfont_data(") + print(" # %s" % os.path.basename(font)) + print(" BytesIO(base64.decodestring(b'''") base64.encode(open(font + ".pil", "rb"), sys.stdout) - print "''')), Image.open(StringIO(base64.decodestring('''" + print("''')), Image.open(BytesIO(base64.decodestring(b'''") base64.encode(open(font + ".pbm", "rb"), sys.stdout) - print "'''))))" + print("'''))))") diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py index 9bcd804d3..6543b0d88 100644 --- a/PIL/ImageGrab.py +++ b/PIL/ImageGrab.py @@ -15,7 +15,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image ## # (New in 1.1.3) The ImageGrab module can be used to copy @@ -45,7 +45,7 @@ except AttributeError: def grab(bbox=None): size, data = grabber() - im = Image.fromstring( + im = Image.frombytes( "RGB", size, data, # RGB, 32-bit line padding, origo in lower left corner "raw", "BGR", (size[0]*3 + 3) & -4, -1 @@ -65,7 +65,8 @@ def grab(bbox=None): def grabclipboard(): debug = 0 # temporary interface data = Image.core.grabclipboard(debug) - if Image.isStringType(data): - import BmpImagePlugin, StringIO - return BmpImagePlugin.DibImageFile(StringIO.StringIO(data)) + if isinstance(data, bytes): + from . import BmpImagePlugin + import io + return BmpImagePlugin.DibImageFile(io.BytesIO(data)) return data diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index 56c42a45a..503306034 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -15,13 +15,20 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image import _imagingmath +import sys + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ VERBOSE = 0 def _isconstant(v): - return isinstance(v, type(0)) or isinstance(v, type(0.0)) + return isinstance(v, int) or isinstance(v, float) class _Operand: # wraps an image operand, providing standard operators @@ -38,7 +45,7 @@ class _Operand: elif im1.im.mode in ("I", "F"): return im1.im else: - raise ValueError, "unsupported mode: %s" % im1.im.mode + raise ValueError("unsupported mode: %s" % im1.im.mode) else: # argument was a constant if _isconstant(im1) and self.im.mode in ("1", "L", "I"): @@ -55,7 +62,7 @@ class _Operand: try: op = getattr(_imagingmath, op+"_"+im1.mode) except AttributeError: - raise TypeError, "bad operand type for '%s'" % op + raise TypeError("bad operand type for '%s'" % op) _imagingmath.unop(op, out.im.id, im1.im.id) else: # binary operation @@ -65,7 +72,7 @@ class _Operand: if im1.mode != "F": im1 = im1.convert("F") if im2.mode != "F": im2 = im2.convert("F") if im1.mode != im2.mode: - raise ValueError, "mode mismatch" + raise ValueError("mode mismatch") if im1.size != im2.size: # crop both arguments to a common size size = (min(im1.size[0], im2.size[0]), @@ -79,14 +86,20 @@ class _Operand: try: op = getattr(_imagingmath, op+"_"+im1.mode) except AttributeError: - raise TypeError, "bad operand type for '%s'" % op + raise TypeError("bad operand type for '%s'" % op) _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id) return _Operand(out) # unary operators - def __nonzero__(self): + def __bool__(self): # an image is "true" if it contains at least one non-zero pixel return self.im.getbbox() is not None + + if bytes is str: + # Provide __nonzero__ for pre-Py3k + __nonzero__ = __bool__ + del __bool__ + def __abs__(self): return self.apply("abs", self) def __pos__(self): @@ -107,9 +120,9 @@ class _Operand: return self.apply("mul", self, other) def __rmul__(self, other): return self.apply("mul", other, self) - def __div__(self, other): + def __truediv__(self, other): return self.apply("div", self, other) - def __rdiv__(self, other): + def __rtruediv__(self, other): return self.apply("div", other, self) def __mod__(self, other): return self.apply("mod", self, other) @@ -120,6 +133,13 @@ class _Operand: def __rpow__(self, other): return self.apply("pow", other, self) + if bytes is str: + # Provide __div__ and __rdiv__ for pre-Py3k + __div__ = __truediv__ + __rdiv__ = __rtruediv__ + del __truediv__ + del __rtruediv__ + # bitwise def __invert__(self): return self.apply("invert", self) @@ -175,7 +195,7 @@ def imagemath_convert(self, mode): return _Operand(self.im.convert(mode)) ops = {} -for k, v in globals().items(): +for k, v in list(globals().items()): if k[:10] == "imagemath_": ops[k[10:]] = v @@ -195,12 +215,11 @@ def eval(expression, _dict={}, **kw): args = ops.copy() args.update(_dict) args.update(kw) - for k, v in args.items(): + for k, v in list(args.items()): if hasattr(v, "im"): args[k] = _Operand(v) - import __builtin__ - out =__builtin__.eval(expression, args) + out = builtins.eval(expression, args) try: return out.im except AttributeError: diff --git a/PIL/ImageMode.py b/PIL/ImageMode.py index 1d5df1c6d..58daa999e 100644 --- a/PIL/ImageMode.py +++ b/PIL/ImageMode.py @@ -36,7 +36,7 @@ class ModeDescriptor: def getmode(mode): if not _modes: # initialize mode cache - import Image + from . import Image # core modes for m, (basemode, basetype, bands) in Image._MODEINFO.items(): _modes[m] = ModeDescriptor(m, bands, basemode, basetype) diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index b51d78e25..4adc59435 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -17,8 +17,9 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image import operator +from functools import reduce ## # (New in 1.1.3) The ImageOps module contains a number of @@ -32,7 +33,7 @@ import operator # helpers def _border(border): - if type(border) is type(()): + if isinstance(border, tuple): if len(border) == 2: left, top = right, bottom = border elif len(border) == 4: @@ -43,7 +44,7 @@ def _border(border): def _color(color, mode): if Image.isStringType(color): - import ImageColor + from . import ImageColor color = ImageColor.getcolor(color, mode) return color @@ -56,7 +57,7 @@ def _lut(image, lut): lut = lut + lut + lut return image.point(lut) else: - raise IOError, "not supported for this image mode" + raise IOError("not supported for this image mode") # # actions @@ -94,7 +95,7 @@ def autocontrast(image, cutoff=0, ignore=None): for ix in range(256): n = n + h[ix] # remove cutoff% pixels from the low end - cut = n * cutoff / 100 + cut = n * cutoff // 100 for lo in range(256): if cut > h[lo]: cut = cut - h[lo] @@ -105,7 +106,7 @@ def autocontrast(image, cutoff=0, ignore=None): if cut <= 0: break # remove cutoff% samples from the hi end - cut = n * cutoff / 100 + cut = n * cutoff // 100 for hi in range(255, -1, -1): if cut > h[hi]: cut = cut - h[hi] @@ -124,7 +125,7 @@ def autocontrast(image, cutoff=0, ignore=None): break if hi <= lo: # don't bother - lut.extend(range(256)) + lut.extend(list(range(256))) else: scale = 255.0 / (hi - lo) offset = -lo * scale @@ -155,9 +156,9 @@ def colorize(image, black, white): white = _color(white, "RGB") red = []; green = []; blue = [] for i in range(256): - red.append(black[0]+i*(white[0]-black[0])/255) - green.append(black[1]+i*(white[1]-black[1])/255) - blue.append(black[2]+i*(white[2]-black[2])/255) + red.append(black[0]+i*(white[0]-black[0])//255) + green.append(black[1]+i*(white[1]-black[1])//255) + blue.append(black[2]+i*(white[2]-black[2])//255) image = image.convert("RGB") return _lut(image, red + green + blue) @@ -209,17 +210,17 @@ def equalize(image, mask=None): h = image.histogram(mask) lut = [] for b in range(0, len(h), 256): - histo = filter(None, h[b:b+256]) + histo = [_f for _f in h[b:b+256] if _f] if len(histo) <= 1: - lut.extend(range(256)) + lut.extend(list(range(256))) else: - step = (reduce(operator.add, histo) - histo[-1]) / 255 + step = (reduce(operator.add, histo) - histo[-1]) // 255 if not step: - lut.extend(range(256)) + lut.extend(list(range(256))) else: - n = step / 2 + n = step // 2 for i in range(256): - lut.append(n / step) + lut.append(n // step) n = n + h[i+b] return _lut(image, lut) @@ -274,7 +275,7 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): # http://www.cazabon.com # ensure inputs are valid - if type(centering) != type([]): + if not isinstance(centering, list): centering = [centering[0], centering[1]] if centering[0] > 1.0 or centering[0] < 0.0: diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 6efee2998..689910521 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -17,7 +17,7 @@ # import array -import Image, ImageColor +from . import Image, ImageColor ## # Colour palette wrapper for palette mapped images. @@ -28,38 +28,42 @@ class ImagePalette: def __init__(self, mode = "RGB", palette = None): self.mode = mode self.rawmode = None # if set, palette contains raw data - self.palette = palette or range(256)*len(self.mode) + self.palette = palette or list(range(256))*len(self.mode) self.colors = {} self.dirty = None if len(self.mode)*256 != len(self.palette): - raise ValueError, "wrong palette size" + raise ValueError("wrong palette size") def getdata(self): # experimental: get palette contents in format suitable # for the low-level im.putpalette primitive if self.rawmode: return self.rawmode, self.palette - return self.mode + ";L", self.tostring() + return self.mode + ";L", self.tobytes() - def tostring(self): - # experimental: convert palette to string + def tobytes(self): + # experimental: convert palette to bytes if self.rawmode: raise ValueError("palette contains raw palette data") - if Image.isStringType(self.palette): + if isinstance(self.palette, bytes): return self.palette return array.array("B", self.palette).tostring() + if bytes is str: + # Declare tostring as an alias for tobytes + tostring = tobytes + def getcolor(self, color): # experimental: given an rgb tuple, allocate palette entry if self.rawmode: raise ValueError("palette contains raw palette data") - if Image.isTupleType(color): + if isinstance(color, tuple): try: return self.colors[color] except KeyError: # allocate new color slot - if Image.isStringType(self.palette): - self.palette = map(int, self.palette) + if isinstance(self.palette, bytes): + self.palette = [int(x) for x in self.palette] index = len(self.colors) if index >= 256: raise ValueError("cannot allocate more than 256 colors") @@ -76,7 +80,7 @@ class ImagePalette: # (experimental) save palette to text file if self.rawmode: raise ValueError("palette contains raw palette data") - if type(fp) == type(""): + if isinstance(fp, str): fp = open(fp, "w") fp.write("# Palette\n") fp.write("# Mode: %s\n" % self.mode) @@ -104,7 +108,7 @@ def _make_linear_lut(black, white): lut = [] if black == 0: for i in range(256): - lut.append(white*i/255) + lut.append(white*i//255) else: raise NotImplementedError # FIXME return lut @@ -119,7 +123,7 @@ def new(mode, data): return Image.core.new_palette(mode, data) def negative(mode="RGB"): - palette = range(256) + palette = list(range(256)) palette.reverse() return ImagePalette(mode, palette * len(mode)) @@ -138,7 +142,7 @@ def sepia(white="#fff0c0"): return ImagePalette("RGB", r + g + b) def wedge(mode="RGB"): - return ImagePalette(mode, range(256) * len(mode)) + return ImagePalette(mode, list(range(256)) * len(mode)) def load(filename): @@ -150,33 +154,39 @@ def load(filename): if not lut: try: - import GimpPaletteFile + from . import GimpPaletteFile fp.seek(0) p = GimpPaletteFile.GimpPaletteFile(fp) lut = p.getpalette() except (SyntaxError, ValueError): + #import traceback + #traceback.print_exc() pass if not lut: try: - import GimpGradientFile + from . import GimpGradientFile fp.seek(0) p = GimpGradientFile.GimpGradientFile(fp) lut = p.getpalette() except (SyntaxError, ValueError): + #import traceback + #traceback.print_exc() pass if not lut: try: - import PaletteFile + from . import PaletteFile fp.seek(0) p = PaletteFile.PaletteFile(fp) lut = p.getpalette() except (SyntaxError, ValueError): + import traceback + traceback.print_exc() pass if not lut: - raise IOError, "cannot load palette" + raise IOError("cannot load palette") return lut # data, rawmode diff --git a/PIL/ImagePath.py b/PIL/ImagePath.py index 721fd9449..fe280ff29 100644 --- a/PIL/ImagePath.py +++ b/PIL/ImagePath.py @@ -14,7 +14,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image ## # Path wrapper. diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 50ee07d6c..9cc2cfad6 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -15,7 +15,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image from PyQt4.QtGui import QImage, qRgb @@ -62,11 +62,11 @@ class ImageQt(QImage): for i in range(0, len(palette), 3): colortable.append(rgb(*palette[i:i+3])) elif im.mode == "RGB": - data = im.tostring("raw", "BGRX") + data = im.tobytes("raw", "BGRX") format = QImage.Format_RGB32 elif im.mode == "RGBA": try: - data = im.tostring("raw", "BGRA") + data = im.tobytes("raw", "BGRA") except SystemError: # workaround for earlier versions r, g, b, a = im.split() @@ -76,7 +76,7 @@ class ImageQt(QImage): raise ValueError("unsupported image mode %r" % im.mode) # must keep a reference, or Qt will crash! - self.__data = data or im.tostring() + self.__data = data or im.tobytes() QImage.__init__(self, self.__data, im.size[0], im.size[1], format) diff --git a/PIL/ImageShow.py b/PIL/ImageShow.py index 05270f440..a5275def4 100644 --- a/PIL/ImageShow.py +++ b/PIL/ImageShow.py @@ -12,7 +12,9 @@ # See the README file for information on usage and redistribution. # -import Image +from __future__ import print_function + +from . import Image import os, sys _viewers = [] @@ -160,4 +162,4 @@ else: if __name__ == "__main__": # usage: python ImageShow.py imagefile [title] - print show(Image.open(sys.argv[1]), *sys.argv[2:]) + print(show(Image.open(sys.argv[1]), *sys.argv[2:])) diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index 9ebdab030..1c0340537 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -21,8 +21,9 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image import operator, math +from functools import reduce ## # The ImageStat module calculates global statistics for an @@ -52,14 +53,14 @@ class Stat: self.h = image_or_list.histogram() except AttributeError: self.h = image_or_list # assume it to be a histogram list - if type(self.h) != type([]): - raise TypeError, "first argument must be image or list" - self.bands = range(len(self.h) / 256) + if not isinstance(self.h, list): + raise TypeError("first argument must be image or list") + self.bands = list(range(len(self.h) // 256)) def __getattr__(self, id): "Calculate missing attribute" if id[:4] == "_get": - raise AttributeError, id + raise AttributeError(id) # calculate missing attribute v = getattr(self, "_get" + id)() setattr(self, id, v) @@ -126,7 +127,7 @@ class Stat: v = [] for i in self.bands: s = 0 - l = self.count[i]/2 + l = self.count[i]//2 b = i * 256 for j in range(256): s = s + self.h[b+j] diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py index 8618139a5..34832d8d9 100644 --- a/PIL/ImageTk.py +++ b/PIL/ImageTk.py @@ -25,7 +25,14 @@ # See the README file for information on usage and redistribution. # -import Tkinter, Image +try: + import tkinter +except ImportError: + import Tkinter + tkinter = Tkinter + del Tkinter + +from . import Image ## # The ImageTk module contains support to create and modify @@ -45,9 +52,9 @@ def _pilbitmap_check(): if _pilbitmap_ok is None: try: im = Image.new("1", (1,1)) - Tkinter.BitmapImage(data="PIL:%d" % im.im.id) + tkinter.BitmapImage(data="PIL:%d" % im.im.id) _pilbitmap_ok = 1 - except Tkinter.TclError: + except tkinter.TclError: _pilbitmap_ok = 0 return _pilbitmap_ok @@ -81,12 +88,12 @@ class PhotoImage: # Tk compatibility: file or data if image is None: - if kw.has_key("file"): + if "file" in kw: image = Image.open(kw["file"]) del kw["file"] - elif kw.has_key("data"): - from StringIO import StringIO - image = Image.open(StringIO(kw["data"])) + elif "data" in kw: + from io import BytesIO + image = Image.open(BytesIO(kw["data"])) del kw["data"] if hasattr(image, "mode") and hasattr(image, "size"): @@ -110,7 +117,7 @@ class PhotoImage: self.__mode = mode self.__size = size - self.__photo = apply(Tkinter.PhotoImage, (), kw) + self.__photo = tkinter.PhotoImage(**kw) self.tk = self.__photo.tk if image: self.paste(image) @@ -175,7 +182,7 @@ class PhotoImage: try: tk.call("PyImagingPhoto", self.__photo, block.id) - except Tkinter.TclError, v: + except tkinter.TclError as v: # activate Tkinter hook try: import _imagingtk @@ -184,7 +191,7 @@ class PhotoImage: except AttributeError: _imagingtk.tkinit(id(tk), 0) tk.call("PyImagingPhoto", self.__photo, block.id) - except (ImportError, AttributeError, Tkinter.TclError): + except (ImportError, AttributeError, tkinter.TclError): raise # configuration problem; cannot attach to Tkinter # -------------------------------------------------------------------- @@ -213,12 +220,12 @@ class BitmapImage: # Tk compatibility: file or data if image is None: - if kw.has_key("file"): + if "file" in kw: image = Image.open(kw["file"]) del kw["file"] - elif kw.has_key("data"): - from StringIO import StringIO - image = Image.open(StringIO(kw["data"])) + elif "data" in kw: + from io import BytesIO + image = Image.open(BytesIO(kw["data"])) del kw["data"] self.__mode = image.mode @@ -232,7 +239,7 @@ class BitmapImage: else: # slow but safe way kw["data"] = image.tobitmap() - self.__photo = apply(Tkinter.BitmapImage, (), kw) + self.__photo = tkinter.BitmapImage(**kw) def __del__(self): name = self.__photo.name @@ -279,18 +286,18 @@ def getimage(photo): def _show(image, title): - class UI(Tkinter.Label): + class UI(tkinter.Label): def __init__(self, master, im): if im.mode == "1": self.image = BitmapImage(im, foreground="white", master=master) else: self.image = PhotoImage(im, master=master) - Tkinter.Label.__init__(self, master, image=self.image, + tkinter.Label.__init__(self, master, image=self.image, bg="black", bd=0) - if not Tkinter._default_root: - raise IOError, "tkinter not initialized" - top = Tkinter.Toplevel() + if not tkinter._default_root: + raise IOError("tkinter not initialized") + top = tkinter.Toplevel() if title: top.title(title) UI(top, image).pack() diff --git a/PIL/ImageTransform.py b/PIL/ImageTransform.py index cc323d3b9..12e3aaa57 100644 --- a/PIL/ImageTransform.py +++ b/PIL/ImageTransform.py @@ -13,7 +13,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image class Transform(Image.ImageTransformHandler): def __init__(self, data): diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py index f98725f74..3d342b409 100644 --- a/PIL/ImageWin.py +++ b/PIL/ImageWin.py @@ -17,7 +17,7 @@ # See the README file for information on usage and redistribution. # -import Image +from . import Image ## # The ImageWin module contains support to create and display @@ -151,22 +151,25 @@ class Dib: self.image.paste(im.im) ## - # Load display memory contents from string buffer. + # Load display memory contents from byte data. # - # @param buffer A string buffer containing display data (usually - # data returned from tostring) + # @param buffer A buffer containing display data (usually + # data returned from tobytes) - def fromstring(self, buffer): - return self.image.fromstring(buffer) + def frombytes(self, buffer): + return self.image.frombytes(buffer) ## - # Copy display memory contents to string buffer. + # Copy display memory contents to bytes object. # - # @return A string buffer containing display data. + # @return A bytes object containing display data. - def tostring(self): - return self.image.tostring() + def tobytes(self): + return self.image.tobytes() + if bytes is str: + tostring = tobytes + fromstring = frombytes ## # Create a Window with the given title size. @@ -179,7 +182,7 @@ class Window: ) def __dispatcher(self, action, *args): - return apply(getattr(self, "ui_handle_" + action), args) + return getattr(self, "ui_handle_" + action)(*args) def ui_handle_clear(self, dc, x0, y0, x1, y1): pass diff --git a/PIL/ImtImagePlugin.py b/PIL/ImtImagePlugin.py index bf5611b8a..7620933a6 100644 --- a/PIL/ImtImagePlugin.py +++ b/PIL/ImtImagePlugin.py @@ -19,12 +19,12 @@ __version__ = "0.2" import re -import Image, ImageFile +from . import Image, ImageFile # # -------------------------------------------------------------------- -field = re.compile(r"([a-z]*) ([^ \r\n]*)") +field = re.compile(br"([a-z]*) ([^ \r\n]*)") ## # Image plugin for IM Tools images. @@ -39,19 +39,19 @@ class ImtImageFile(ImageFile.ImageFile): # Quick rejection: if there's not a LF among the first # 100 bytes, this is (probably) not a text header. - if not "\n" in self.fp.read(100): - raise SyntaxError, "not an IM file" + if not b"\n" in self.fp.read(100): + raise SyntaxError("not an IM file") self.fp.seek(0) xsize = ysize = 0 - while 1: + while True: s = self.fp.read(1) if not s: break - if s == chr(12): + if s == b'\x0C': # image data begins self.tile = [("raw", (0,0)+self.size, @@ -67,7 +67,7 @@ class ImtImageFile(ImageFile.ImageFile): s = s + self.fp.readline() if len(s) == 1 or len(s) > 100: break - if s[0] == "*": + if s[0] == b"*": continue # comment m = field.match(s) diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index acea5d18b..c9bca964e 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -15,37 +15,36 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function __version__ = "0.3" -import Image, ImageFile +from . import Image, ImageFile, _binary import os, tempfile +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be +o8 = _binary.o8 COMPRESSION = { 1: "raw", 5: "jpeg" } -PAD = chr(0) * 4 +PAD = o8(0) * 4 # # Helpers -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) - def i(c): return i32((PAD + c)[-4:]) def dump(c): for i in c: - print "%02x" % ord(i), - print + print("%02x" % i8(i), end=' ') + print() ## # Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields @@ -66,16 +65,16 @@ class IptcImageFile(ImageFile.ImageFile): if not len(s): return None, 0 - tag = ord(s[1]), ord(s[2]) + tag = i8(s[1]), i8(s[2]) # syntax - if ord(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9: - raise SyntaxError, "invalid IPTC/NAA file" + if i8(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9: + raise SyntaxError("invalid IPTC/NAA file") # field size - size = ord(s[3]) + size = i8(s[3]) if size > 132: - raise IOError, "illegal field length in IPTC/NAA file" + raise IOError("illegal field length in IPTC/NAA file") elif size == 128: size = 0 elif size > 128: @@ -97,7 +96,7 @@ class IptcImageFile(ImageFile.ImageFile): if sz != size[0]: return 0 y = 1 - while 1: + while True: self.fp.seek(sz, 1) t, s = self.field() if t != (8, 10): @@ -110,7 +109,7 @@ class IptcImageFile(ImageFile.ImageFile): def _open(self): # load descriptive fields - while 1: + while True: offset = self.fp.tell() tag, size = self.field() if not tag or tag == (8,10): @@ -119,7 +118,7 @@ class IptcImageFile(ImageFile.ImageFile): tagdata = self.fp.read(size) else: tagdata = None - if tag in self.info.keys(): + if tag in list(self.info.keys()): if isinstance(self.info[tag], list): self.info[tag].append(tagdata) else: @@ -130,10 +129,10 @@ class IptcImageFile(ImageFile.ImageFile): # print tag, self.info[tag] # mode - layers = ord(self.info[(3,60)][0]) - component = ord(self.info[(3,60)][1]) - if self.info.has_key((3,65)): - id = ord(self.info[(3,65)][0])-1 + layers = i8(self.info[(3,60)][0]) + component = i8(self.info[(3,60)][1]) + if (3,65) in self.info: + id = i8(self.info[(3,65)][0])-1 else: id = 0 if layers == 1 and not component: @@ -150,7 +149,7 @@ class IptcImageFile(ImageFile.ImageFile): try: compression = COMPRESSION[self.getint((3,120))] except KeyError: - raise IOError, "Unknown IPTC image compression" + raise IOError("Unknown IPTC image compression") # tile if tag == (8,10): @@ -179,7 +178,7 @@ class IptcImageFile(ImageFile.ImageFile): # To simplify access to the extracted file, # prepend a PPM header o.write("P5\n%d %d\n255\n" % self.size) - while 1: + while True: type, size = self.field() if type != (8, 10): break @@ -218,8 +217,8 @@ Image.register_extension("IPTC", ".iim") def getiptcinfo(im): - import TiffImagePlugin, JpegImagePlugin - import StringIO + from . import TiffImagePlugin, JpegImagePlugin + import io data = None @@ -241,7 +240,7 @@ def getiptcinfo(im): code = JpegImagePlugin.i16(app, offset) offset = offset + 2 # resource name (usually empty) - name_len = ord(app[offset]) + name_len = i8(app[offset]) name = app[offset+1:offset+1+name_len] offset = 1 + offset + name_len if offset & 1: @@ -278,7 +277,7 @@ def getiptcinfo(im): # parse the IPTC information chunk im.info = {} - im.fp = StringIO.StringIO(data) + im.fp = io.BytesIO(data) try: im._open() diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 933abf3c2..aa6745ee8 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -35,14 +35,12 @@ __version__ = "0.6" import array, struct -import string -import Image, ImageFile +from . import Image, ImageFile, _binary -def i16(c,o=0): - return ord(c[o+1]) + (ord(c[o])<<8) - -def i32(c,o=0): - return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24) +i8 = _binary.i8 +o8 = _binary.o8 +i16 = _binary.i16be +i32 = _binary.i32be # # Parser @@ -64,13 +62,13 @@ def APP(self, marker): self.app[app] = s # compatibility self.applist.append((app, s)) - if marker == 0xFFE0 and s[:4] == "JFIF": + if marker == 0xFFE0 and s[:4] == b"JFIF": # extract JFIF information self.info["jfif"] = version = i16(s, 5) # version self.info["jfif_version"] = divmod(version, 256) # extract JFIF properties try: - jfif_unit = ord(s[7]) + jfif_unit = i8(s[7]) jfif_density = i16(s, 8), i16(s, 10) except: pass @@ -79,13 +77,13 @@ def APP(self, marker): self.info["dpi"] = jfif_density self.info["jfif_unit"] = jfif_unit self.info["jfif_density"] = jfif_density - elif marker == 0xFFE1 and s[:5] == "Exif\0": + elif marker == 0xFFE1 and s[:5] == b"Exif\0": # extract Exif information (incomplete) self.info["exif"] = s # FIXME: value will change - elif marker == 0xFFE2 and s[:5] == "FPXR\0": + elif marker == 0xFFE2 and s[:5] == b"FPXR\0": # extract FlashPix information (incomplete) self.info["flashpix"] = s # FIXME: value will change - elif marker == 0xFFE2 and s[:12] == "ICC_PROFILE\0": + elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0": # Since an ICC profile can be larger than the maximum size of # a JPEG marker (64K), we need provisions to split it into # multiple markers. The format defined by the ICC specifies @@ -98,11 +96,11 @@ def APP(self, marker): # reassemble the profile, rather than assuming that the APP2 # markers appear in the correct sequence. self.icclist.append(s) - elif marker == 0xFFEE and s[:5] == "Adobe": + elif marker == 0xFFEE and s[:5] == b"Adobe": self.info["adobe"] = i16(s, 5) # extract Adobe custom properties try: - adobe_transform = ord(s[1]) + adobe_transform = i8(s[1]) except: pass else: @@ -130,11 +128,11 @@ def SOF(self, marker): s = ImageFile._safe_read(self.fp, n) self.size = i16(s[3:]), i16(s[1:]) - self.bits = ord(s[0]) + self.bits = i8(s[0]) if self.bits != 8: raise SyntaxError("cannot handle %d-bit layers" % self.bits) - self.layers = ord(s[5]) + self.layers = i8(s[5]) if self.layers == 1: self.mode = "L" elif self.layers == 3: @@ -150,11 +148,11 @@ def SOF(self, marker): if self.icclist: # fixup icc profile self.icclist.sort() # sort by sequence number - if ord(self.icclist[0][13]) == len(self.icclist): + if i8(self.icclist[0][13]) == len(self.icclist): profile = [] for p in self.icclist: profile.append(p[14:]) - icc_profile = string.join(profile, "") + icc_profile = b"".join(profile) else: icc_profile = None # wrong number of fragments self.info["icc_profile"] = icc_profile @@ -163,7 +161,7 @@ def SOF(self, marker): for i in range(6, len(s), 3): t = s[i:i+3] # 4-tuples: id, vsamp, hsamp, qtable - self.layer.append((t[0], ord(t[1])/16, ord(t[1])&15, ord(t[2]))) + self.layer.append((t[0], i8(t[1])//16, i8(t[1])&15, i8(t[2]))) def DQT(self, marker): # @@ -179,8 +177,8 @@ def DQT(self, marker): while len(s): if len(s) < 65: raise SyntaxError("bad quantization table marker") - v = ord(s[0]) - if v/16 == 0: + v = i8(s[0]) + if v//16 == 0: self.quantization[v&15] = array.array("b", s[1:65]) s = s[65:] else: @@ -259,7 +257,7 @@ MARKER = { def _accept(prefix): - return prefix[0] == "\377" + return prefix[0:1] == b"\377" ## # Image plugin for JPEG and JFIF images. @@ -273,7 +271,7 @@ class JpegImageFile(ImageFile.ImageFile): s = self.fp.read(1) - if ord(s[0]) != 255: + if i8(s[0]) != 255: raise SyntaxError("not a JPEG file") # Create attributes @@ -288,13 +286,13 @@ class JpegImageFile(ImageFile.ImageFile): self.applist = [] self.icclist = [] - while 1: + while True: s = s + self.fp.read(1) i = i16(s) - if MARKER.has_key(i): + if i in MARKER: name, description, handler = MARKER[i] # print hex(i), name, description if handler is not None: @@ -326,12 +324,12 @@ class JpegImageFile(ImageFile.ImageFile): a = mode, "" if size: - scale = max(self.size[0] / size[0], self.size[1] / size[1]) + scale = max(self.size[0] // size[0], self.size[1] // size[1]) for s in [8, 4, 2, 1]: if scale >= s: break - e = e[0], e[1], (e[2]-e[0]+s-1)/s+e[0], (e[3]-e[1]+s-1)/s+e[1] - self.size = ((self.size[0]+s-1)/s, (self.size[1]+s-1)/s) + e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1] + self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s) scale = s self.tile = [(d, e, o, a)] @@ -362,7 +360,8 @@ class JpegImageFile(ImageFile.ImageFile): # Extract EXIF information. This method is highly experimental, # and is likely to be replaced with something better in a future # version. - import TiffImagePlugin, StringIO + from . import TiffImagePlugin + import io def fixup(value): if len(value) == 1: return value[0] @@ -373,7 +372,7 @@ class JpegImageFile(ImageFile.ImageFile): data = self.info["exif"] except KeyError: return None - file = StringIO.StringIO(data[6:]) + file = io.BytesIO(data[6:]) head = file.read(8) exif = {} # process dictionary @@ -436,7 +435,7 @@ def _save(im, fp, filename): elif subsampling == "4:1:1": subsampling = 2 - extra = "" + extra = b"" icc_profile = info.get("icc_profile") if icc_profile: @@ -450,7 +449,7 @@ def _save(im, fp, filename): i = 1 for marker in markers: size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker)) - extra = extra + ("\xFF\xE2" + size + "ICC_PROFILE\0" + chr(i) + chr(len(markers)) + marker) + extra = extra + (b"\xFF\xE2" + size + b"ICC_PROFILE\0" + o8(i) + o8(len(markers)) + marker) i = i + 1 # get keyword arguments @@ -459,9 +458,9 @@ def _save(im, fp, filename): # "progressive" is the official name, but older documentation # says "progression" # FIXME: issue a warning if the wrong form is used (post-1.1.7) - info.has_key("progressive") or info.has_key("progression"), + "progressive" in info or "progression" in info, info.get("smooth", 0), - info.has_key("optimize"), + "optimize" in info, info.get("streamtype", 0), dpi[0], dpi[1], subsampling, diff --git a/PIL/McIdasImagePlugin.py b/PIL/McIdasImagePlugin.py index 62ee52cf8..36982d7de 100644 --- a/PIL/McIdasImagePlugin.py +++ b/PIL/McIdasImagePlugin.py @@ -19,10 +19,10 @@ __version__ = "0.2" import struct -import Image, ImageFile +from . import Image, ImageFile def _accept(s): - return s[:8] == "\x00\x00\x00\x00\x00\x00\x00\x04" + return s[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" ## # Image plugin for McIdas area images. diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index b1a3bba79..b8ee55e06 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -20,8 +20,8 @@ __version__ = "0.1" -import Image, TiffImagePlugin -from OleFileIO import * +from . import Image, TiffImagePlugin +from .OleFileIO import * # @@ -47,7 +47,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): try: self.ole = OleFileIO(self.fp) except IOError: - raise SyntaxError, "not an MIC file; invalid OLE file" + raise SyntaxError("not an MIC file; invalid OLE file") # find ACI subfiles with Image members (maybe not the # best way to identify MIC files, but what the... ;-) @@ -60,7 +60,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): # if we didn't find any images, this is probably not # an MIC file. if not self.images: - raise SyntaxError, "not an MIC file; no image entries" + raise SyntaxError("not an MIC file; no image entries") self.__fp = self.fp self.frame = 0 @@ -75,7 +75,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): try: filename = self.images[frame] except IndexError: - raise EOFError, "no such frame" + raise EOFError("no such frame") self.fp = self.ole.openstream(filename) diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py index c02edee82..7c1fac273 100644 --- a/PIL/MpegImagePlugin.py +++ b/PIL/MpegImagePlugin.py @@ -15,7 +15,8 @@ __version__ = "0.1" -import Image, ImageFile +from . import Image, ImageFile +from ._binary import i8 # # Bitstream parser @@ -28,7 +29,7 @@ class BitStream: self.bitbuffer = 0 def next(self): - return ord(self.fp.read(1)) + return i8(self.fp.read(1)) def peek(self, bits): while self.bits < bits: @@ -38,11 +39,11 @@ class BitStream: continue self.bitbuffer = (self.bitbuffer << 8) + c self.bits = self.bits + 8 - return self.bitbuffer >> (self.bits - bits) & (1L << bits) - 1 + return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1 def skip(self, bits): while self.bits < bits: - self.bitbuffer = (self.bitbuffer << 8) + ord(self.fp.read(1)) + self.bitbuffer = (self.bitbuffer << 8) + i8(self.fp.read(1)) self.bits = self.bits + 8 self.bits = self.bits - bits @@ -65,7 +66,7 @@ class MpegImageFile(ImageFile.ImageFile): s = BitStream(self.fp) if s.read(32) != 0x1B3: - raise SyntaxError, "not an MPEG file" + raise SyntaxError("not an MPEG file") self.mode = "RGB" self.size = s.read(12), s.read(12) diff --git a/PIL/MspImagePlugin.py b/PIL/MspImagePlugin.py index 9dac36b47..a6728067b 100644 --- a/PIL/MspImagePlugin.py +++ b/PIL/MspImagePlugin.py @@ -19,17 +19,16 @@ __version__ = "0.1" -import Image, ImageFile +from . import Image, ImageFile, _binary # # read MSP files -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) +i16 = _binary.i16le def _accept(prefix): - return prefix[:4] in ["DanM", "LinS"] + return prefix[:4] in [b"DanM", b"LinS"] ## # Image plugin for Windows MSP images. This plugin supports both @@ -44,20 +43,20 @@ class MspImageFile(ImageFile.ImageFile): # Header s = self.fp.read(32) - if s[:4] not in ["DanM", "LinS"]: - raise SyntaxError, "not an MSP file" + if s[:4] not in [b"DanM", b"LinS"]: + raise SyntaxError("not an MSP file") # Header checksum sum = 0 for i in range(0, 32, 2): sum = sum ^ i16(s[i:i+2]) if sum != 0: - raise SyntaxError, "bad MSP checksum" + raise SyntaxError("bad MSP checksum") self.mode = "1" self.size = i16(s[4:]), i16(s[6:]) - if s[:4] == "DanM": + if s[:4] == b"DanM": self.tile = [("raw", (0,0)+self.size, 32, ("1", 0, 1))] else: self.tile = [("msp", (0,0)+self.size, 32+2*self.size[1], None)] @@ -65,18 +64,17 @@ class MspImageFile(ImageFile.ImageFile): # # write MSP files (uncompressed only) -def o16(i): - return chr(i&255) + chr(i>>8&255) +o16 = _binary.o16le def _save(im, fp, filename): if im.mode != "1": - raise IOError, "cannot write mode %s as MSP" % im.mode + raise IOError("cannot write mode %s as MSP" % im.mode) # create MSP header header = [0] * 16 - header[0], header[1] = i16("Da"), i16("nM") # version 1 + header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1 header[2], header[3] = im.size header[4], header[5] = 1, 1 header[6], header[7] = 1, 1 diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 36e598a9b..d5d265c6a 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -36,17 +36,21 @@ # See the README file for information on usage and redistribution. # -import string, StringIO +from __future__ import print_function + +import io +import sys +from . import _binary + +if str is not bytes: + long = int + +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le -def i16(c, o = 0): - return ord(c[o])+(ord(c[o+1])<<8) - -def i32(c, o = 0): - return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24) - - -MAGIC = '\320\317\021\340\241\261\032\341' +MAGIC = b'\320\317\021\340\241\261\032\341' # # -------------------------------------------------------------------- @@ -65,7 +69,7 @@ VT_VECTOR=0x1000; # map property id to name (for debugging purposes) VT = {} -for k, v in vars().items(): +for k, v in list(vars().items()): if k[:3] == "VT_": VT[v] = k @@ -79,7 +83,7 @@ WORD_CLSID = "00020900-0000-0000-C000-000000000046" # # -------------------------------------------------------------------- -class _OleStream(StringIO.StringIO): +class _OleStream(io.BytesIO): """OLE2 Stream @@ -105,11 +109,11 @@ class _OleStream(StringIO.StringIO): data.append(fp.read(sectorsize)) sect = fat[sect] - data = string.join(data, "") + data = b"".join(data) # print len(data), size - StringIO.StringIO.__init__(self, data[:size]) + io.BytesIO.__init__(self, data[:size]) # # -------------------------------------------------------------------- @@ -173,7 +177,7 @@ class _OleDirectoryEntry: if right != -1: # 0xFFFFFFFFL: # and then back to the left sid = right - while 1: + while True: left, right, child = sidlist[sid][4] if left == -1: # 0xFFFFFFFFL: break @@ -181,7 +185,7 @@ class _OleDirectoryEntry: sid = left else: # couldn't move right; move up instead - while 1: + while True: ptr = stack[-1] del stack[-1] left, right, child = sidlist[ptr][4] @@ -208,12 +212,12 @@ class _OleDirectoryEntry: TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)", "(property)", "(root)"] - print " "*tab + repr(self.name), TYPES[self.type], + print(" "*tab + repr(self.name), TYPES[self.type], end=' ') if self.type in (2, 5): - print self.size, "bytes", - print + print(self.size, "bytes", end=' ') + print() if self.type in (1, 5) and self.clsid: - print " "*tab + "{%s}" % self.clsid + print(" "*tab + "{%s}" % self.clsid) for kid in self.kids: kid.dump(tab + 2) @@ -265,7 +269,7 @@ class OleFileIO: def open(self, filename): """Open an OLE2 file""" - if type(filename) == type(""): + if isinstance(filename, str): self.fp = open(filename, "rb") else: self.fp = filename @@ -273,7 +277,7 @@ class OleFileIO: header = self.fp.read(512) if len(header) != 512 or header[:8] != MAGIC: - raise IOError, "not an OLE2 structured storage file" + raise IOError("not an OLE2 structured storage file") # file clsid (probably never used, so we don't store it) clsid = self._clsid(header[8:24]) @@ -307,7 +311,7 @@ class OleFileIO: if ix == -2 or ix == -1: # ix == 0xFFFFFFFEL or ix == 0xFFFFFFFFL: break s = self.getsect(ix) - fat = fat + map(lambda i, s=s: i32(s, i), range(0, len(s), 4)) + fat = fat + [i32(s, i) for i in range(0, len(s), 4)] self.fat = fat def loadminifat(self): @@ -316,7 +320,7 @@ class OleFileIO: s = self._open(self.minifatsect).read() - self.minifat = map(lambda i, s=s: i32(s, i), range(0, len(s), 4)) + self.minifat = [i32(s, i) for i in range(0, len(s), 4)] def getsect(self, sect): # Read given sector @@ -327,9 +331,12 @@ class OleFileIO: def _unicode(self, s): # Map unicode string to Latin 1 - # FIXME: some day, Python will provide an official way to handle - # Unicode strings, but until then, this will have to do... - return filter(ord, s) + if bytes is str: + # Old version tried to produce a Latin-1 str + return s.decode('utf-16').encode('latin-1', 'replace') + else: + # Provide actual Unicode string + return s.decode('utf-16') def loaddirectory(self, sect): # Load the directory. The directory is stored in a standard @@ -340,11 +347,11 @@ class OleFileIO: # create list of sid entries self.sidlist = [] - while 1: + while True: entry = fp.read(128) if not entry: break - type = ord(entry[66]) + type = i8(entry[66]) name = self._unicode(entry[0:0+i16(entry, 64)]) ptrs = i32(entry, 68), i32(entry, 72), i32(entry, 76) sect, size = i32(entry, 116), i32(entry, 120) @@ -364,7 +371,7 @@ class OleFileIO: return "" return (("%08X-%04X-%04X-%02X%02X-" + "%02X" * 6) % ((i32(clsid, 0), i16(clsid, 4), i16(clsid, 6)) + - tuple(map(ord, clsid[8:16])))) + tuple(map(i8, clsid[8:16])))) def _list(self, files, prefix, node): # listdir helper @@ -385,7 +392,7 @@ class OleFileIO: if kid.name == name: break else: - raise IOError, "file not found" + raise IOError("file not found") node = kid return node.sid @@ -423,7 +430,7 @@ class OleFileIO: slot = self._find(filename) name, type, sect, size, sids, clsid = self.sidlist[slot] if type != 2: - raise IOError, "this file is not a stream" + raise IOError("this file is not a stream") return self._open(sect, size) ## @@ -480,9 +487,9 @@ class OleFileIO: value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32) # FIXME: this is a 64-bit int: "number of 100ns periods # since Jan 1,1601". Should map this to Python time - value = value / 10000000L # seconds + value = value // 10000000 # seconds elif type == VT_UI1: - value = ord(s[offset+4]) + value = i8(s[offset+4]) elif type == VT_CLSID: value = self._clsid(s[offset+4:offset+20]) elif type == VT_CF: @@ -512,17 +519,16 @@ if __name__ == "__main__": for file in sys.argv[1:]: try: ole = OleFileIO(file) - print "-" * 68 - print file - print "-" * 68 + print("-" * 68) + print(file) + print("-" * 68) ole.dumpdirectory() for file in ole.listdir(): if file[-1][0] == "\005": - print file + print(file) props = ole.getproperties(file) - props = props.items() - props.sort() + props = sorted(props.items()) for k, v in props: - print " ", k, v - except IOError, v: - print "***", "cannot read", file, "-", v + print(" ", k, v) + except IOError as v: + print("***", "cannot read", file, "-", v) diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py index 7309e17b3..2679b34ca 100644 --- a/PIL/PSDraw.py +++ b/PIL/PSDraw.py @@ -15,8 +15,9 @@ # See the README file for information on usage and redistribution. # -import EpsImagePlugin -import string +from __future__ import print_function + +from . import EpsImagePlugin ## # Simple Postscript graphics interface. @@ -52,7 +53,7 @@ class PSDraw: self.fp.flush() def setfont(self, font, size): - if not self.isofont.has_key(font): + if font not in self.isofont: # reencode font self.fp.write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %\ (font, font)) @@ -61,7 +62,7 @@ class PSDraw: self.fp.write("/F0 %d /PSDraw-%s F\n" % (size, font)) def setink(self, ink): - print "*** NOT YET IMPLEMENTED ***" + print("*** NOT YET IMPLEMENTED ***") def line(self, xy0, xy1): xy = xy0 + xy1 @@ -71,8 +72,8 @@ class PSDraw: self.fp.write("%d %d M %d %d 0 Vr\n" % box) def text(self, xy, text): - text = string.joinfields(string.splitfields(text, "("), "\\(") - text = string.joinfields(string.splitfields(text, ")"), "\\)") + text = "\\(".join(text.split("(")) + text = "\\)".join(text.split(")")) xy = xy + (text,) self.fp.write("%d %d M (%s) S\n" % xy) diff --git a/PIL/PaletteFile.py b/PIL/PaletteFile.py index 3bbd91327..c6a3f36e2 100644 --- a/PIL/PaletteFile.py +++ b/PIL/PaletteFile.py @@ -13,7 +13,7 @@ # See the README file for information on usage and redistribution. # -import string +from ._binary import o8 ## # File handler for Teragon-style palette files. @@ -24,20 +24,20 @@ class PaletteFile: def __init__(self, fp): - self.palette = map(lambda i: (i, i, i), range(256)) + self.palette = [(i, i, i) for i in range(256)] - while 1: + while True: s = fp.readline() if not s: break - if s[0] == "#": + if s[0:1] == b"#": continue if len(s) > 100: - raise SyntaxError, "bad palette file" + raise SyntaxError("bad palette file") - v = map(int, string.split(s)) + v = [int(x) for x in s.split()] try: [i, r, g, b] = v except ValueError: @@ -45,9 +45,9 @@ class PaletteFile: g = b = r if 0 <= i <= 255: - self.palette[i] = chr(r) + chr(g) + chr(b) + self.palette[i] = o8(r) + o8(g) + o8(b) - self.palette = string.join(self.palette, "") + self.palette = b"".join(self.palette) def getpalette(self): diff --git a/PIL/PalmImagePlugin.py b/PIL/PalmImagePlugin.py index 785023130..4f1be0daa 100644 --- a/PIL/PalmImagePlugin.py +++ b/PIL/PalmImagePlugin.py @@ -9,7 +9,7 @@ __version__ = "1.0" -import Image, ImageFile +from . import Image, ImageFile, _binary _Palm8BitColormapValues = ( ( 255, 255, 255 ), ( 255, 204, 255 ), ( 255, 153, 255 ), ( 255, 102, 255 ), @@ -80,7 +80,7 @@ _Palm8BitColormapValues = ( # so build a prototype image to be used for palette resampling def build_prototype_image(): image = Image.new("L", (1,len(_Palm8BitColormapValues),)) - image.putdata(range(len(_Palm8BitColormapValues))) + image.putdata(list(range(len(_Palm8BitColormapValues)))) palettedata = () for i in range(len(_Palm8BitColormapValues)): palettedata = palettedata + _Palm8BitColormapValues[i] @@ -107,8 +107,8 @@ _COMPRESSION_TYPES = { "scanline": 0x00, } -def o16b(i): - return chr(i>>8&255) + chr(i&255) +o8 = _binary.o8 +o16b = _binary.o16be # # -------------------------------------------------------------------- @@ -127,7 +127,7 @@ def _save(im, fp, filename, check=0): bpp = 8 version = 1 - elif im.mode == "L" and im.encoderinfo.has_key("bpp") and im.encoderinfo["bpp"] in (1, 2, 4): + elif im.mode == "L" and "bpp" in im.encoderinfo and im.encoderinfo["bpp"] in (1, 2, 4): # this is 8-bit grayscale, so we shift it to get the high-order bits, and invert it because # Palm does greyscale from white (0) to black (1) @@ -138,7 +138,7 @@ def _save(im, fp, filename, check=0): rawmode = "P;" + str(bpp) version = 1 - elif im.mode == "L" and im.info.has_key("bpp") and im.info["bpp"] in (1, 2, 4): + elif im.mode == "L" and "bpp" in im.info and im.info["bpp"] in (1, 2, 4): # here we assume that even though the inherent mode is 8-bit grayscale, only # the lower bpp bits are significant. We invert them to match the Palm. @@ -158,7 +158,7 @@ def _save(im, fp, filename, check=0): else: - raise IOError, "cannot write mode %s as Palm" % im.mode + raise IOError("cannot write mode %s as Palm" % im.mode) if check: return check @@ -172,12 +172,12 @@ def _save(im, fp, filename, check=0): cols = im.size[0] rows = im.size[1] - rowbytes = ((cols + (16/bpp - 1)) / (16 / bpp)) * 2; + rowbytes = ((cols + (16//bpp - 1)) / (16 // bpp)) * 2; transparent_index = 0 compression_type = _COMPRESSION_TYPES["none"] flags = 0; - if im.mode == "P" and im.info.has_key("custom-colormap"): + if im.mode == "P" and "custom-colormap" in im.info: flags = flags & _FLAGS["custom-colormap"] colormapsize = 4 * 256 + 2; colormapmode = im.palette.mode @@ -185,17 +185,17 @@ def _save(im, fp, filename, check=0): else: colormapsize = 0 - if im.info.has_key("offset"): - offset = (rowbytes * rows + 16 + 3 + colormapsize) / 4; + if "offset" in im.info: + offset = (rowbytes * rows + 16 + 3 + colormapsize) // 4; else: offset = 0 fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags)) - fp.write(chr(bpp)) - fp.write(chr(version)) + fp.write(o8(bpp)) + fp.write(o8(version)) fp.write(o16b(offset)) - fp.write(chr(transparent_index)) - fp.write(chr(compression_type)) + fp.write(o8(transparent_index)) + fp.write(o8(compression_type)) fp.write(o16b(0)) # reserved by Palm # now write colormap if necessary @@ -203,11 +203,11 @@ def _save(im, fp, filename, check=0): if colormapsize > 0: fp.write(o16b(256)) for i in range(256): - fp.write(chr(i)) + fp.write(o8(i)) if colormapmode == 'RGB': - fp.write(chr(colormap[3 * i]) + chr(colormap[3 * i + 1]) + chr(colormap[3 * i + 2])) + fp.write(o8(colormap[3 * i]) + o8(colormap[3 * i + 1]) + o8(colormap[3 * i + 2])) elif colormapmode == 'RGBA': - fp.write(chr(colormap[4 * i]) + chr(colormap[4 * i + 1]) + chr(colormap[4 * i + 2])) + fp.write(o8(colormap[4 * i]) + o8(colormap[4 * i + 1]) + o8(colormap[4 * i + 2])) # now convert data to raw form ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, rowbytes, 1))]) diff --git a/PIL/PcdImagePlugin.py b/PIL/PcdImagePlugin.py index 07bd27e9a..4ce2b2439 100644 --- a/PIL/PcdImagePlugin.py +++ b/PIL/PcdImagePlugin.py @@ -18,7 +18,9 @@ __version__ = "0.1" -import Image, ImageFile +from . import Image, ImageFile, _binary + +i8 = _binary.i8 ## # Image plugin for PhotoCD images. This plugin only reads the 768x512 @@ -36,10 +38,10 @@ class PcdImageFile(ImageFile.ImageFile): self.fp.seek(2048) s = self.fp.read(2048) - if s[:4] != "PCD_": - raise SyntaxError, "not a PCD file" + if s[:4] != b"PCD_": + raise SyntaxError("not a PCD file") - orientation = ord(s[1538]) & 3 + orientation = i8(s[1538]) & 3 if orientation == 1: self.tile_post_rotate = 90 # hack elif orientation == 3: diff --git a/PIL/PcfFontFile.py b/PIL/PcfFontFile.py index bec39e3a0..ce67025ee 100644 --- a/PIL/PcfFontFile.py +++ b/PIL/PcfFontFile.py @@ -16,10 +16,9 @@ # See the README file for information on usage and redistribution. # -import Image -import FontFile - -import string +from . import Image +from . import FontFile +from . import _binary # -------------------------------------------------------------------- # declarations @@ -43,19 +42,14 @@ BYTES_PER_ROW = [ lambda bits: ((bits+63) >> 3) & ~7, ] - -def l16(c): - return ord(c[0]) + (ord(c[1])<<8) -def l32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) - -def b16(c): - return ord(c[1]) + (ord(c[0])<<8) -def b32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i8 = _binary.i8 +l16 = _binary.i16le +l32 = _binary.i32le +b16 = _binary.i16be +b32 = _binary.i32be def sz(s, o): - return s[o:string.index(s, "\0", o)] + return s[o:s.index(b"\0", o)] ## # Font file plugin for the X11 PCF format. @@ -68,7 +62,7 @@ class PcfFontFile(FontFile.FontFile): magic = l32(fp.read(4)) if magic != PCF_MAGIC: - raise SyntaxError, "not a PCF file" + raise SyntaxError("not a PCF file") FontFile.FontFile.__init__(self) @@ -126,7 +120,7 @@ class PcfFontFile(FontFile.FontFile): # read property description p = [] for i in range(nprops): - p.append((i32(fp.read(4)), ord(fp.read(1)), i32(fp.read(4)))) + p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4)))) if nprops & 3: fp.seek(4 - (nprops & 3), 1) # pad @@ -155,11 +149,11 @@ class PcfFontFile(FontFile.FontFile): # "compressed" metrics for i in range(i16(fp.read(2))): - left = ord(fp.read(1)) - 128 - right = ord(fp.read(1)) - 128 - width = ord(fp.read(1)) - 128 - ascent = ord(fp.read(1)) - 128 - descent = ord(fp.read(1)) - 128 + left = i8(fp.read(1)) - 128 + right = i8(fp.read(1)) - 128 + width = i8(fp.read(1)) - 128 + ascent = i8(fp.read(1)) - 128 + descent = i8(fp.read(1)) - 128 xsize = right - left ysize = ascent + descent append( @@ -198,7 +192,7 @@ class PcfFontFile(FontFile.FontFile): nbitmaps = i32(fp.read(4)) if nbitmaps != len(metrics): - raise IOError, "Wrong number of bitmaps" + raise IOError("Wrong number of bitmaps") offsets = [] for i in range(nbitmaps): @@ -226,7 +220,7 @@ class PcfFontFile(FontFile.FontFile): x, y, l, r, w, a, d, f = metrics[i] b, e = offsets[i], offsets[i+1] bitmaps.append( - Image.fromstring("1", (x, y), data[b:e], "raw", mode, pad(x)) + Image.frombytes("1", (x, y), data[b:e], "raw", mode, pad(x)) ) return bitmaps diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py index 15012d51c..f556ceced 100644 --- a/PIL/PcxImagePlugin.py +++ b/PIL/PcxImagePlugin.py @@ -27,13 +27,14 @@ __version__ = "0.6" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary -def i16(c,o): - return ord(c[o]) + (ord(c[o+1])<<8) +i8 = _binary.i8 +i16 = _binary.i16le +o8 = _binary.o8 def _accept(prefix): - return ord(prefix[0]) == 10 and ord(prefix[1]) in [0, 2, 3, 5] + return i8(prefix[0]) == 10 and i8(prefix[1]) in [0, 2, 3, 5] ## # Image plugin for Paintbrush images. @@ -48,17 +49,17 @@ class PcxImageFile(ImageFile.ImageFile): # header s = self.fp.read(128) if not _accept(s): - raise SyntaxError, "not a PCX file" + raise SyntaxError("not a PCX file") # image bbox = i16(s,4), i16(s,6), i16(s,8)+1, i16(s,10)+1 if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: - raise SyntaxError, "bad PCX image size" + raise SyntaxError("bad PCX image size") # format - version = ord(s[1]) - bits = ord(s[3]) - planes = ord(s[65]) + version = i8(s[1]) + bits = i8(s[3]) + planes = i8(s[65]) stride = i16(s,66) self.info["dpi"] = i16(s,12), i16(s,14) @@ -76,10 +77,10 @@ class PcxImageFile(ImageFile.ImageFile): # FIXME: hey, this doesn't work with the incremental loader !!! self.fp.seek(-769, 2) s = self.fp.read(769) - if len(s) == 769 and ord(s[0]) == 12: + if len(s) == 769 and i8(s[0]) == 12: # check if the palette is linear greyscale for i in range(256): - if s[i*3+1:i*3+4] != chr(i)*3: + if s[i*3+1:i*3+4] != o8(i)*3: mode = rawmode = "P" break if mode == "P": @@ -91,7 +92,7 @@ class PcxImageFile(ImageFile.ImageFile): rawmode = "RGB;L" else: - raise IOError, "unknown PCX mode" + raise IOError("unknown PCX mode") self.mode = mode self.size = bbox[2]-bbox[0], bbox[3]-bbox[1] @@ -111,21 +112,20 @@ SAVE = { "RGB": (5, 8, 3, "RGB;L"), } -def o16(i): - return chr(i&255) + chr(i>>8&255) +o16 = _binary.o16le def _save(im, fp, filename, check=0): try: version, bits, planes, rawmode = SAVE[im.mode] except KeyError: - raise ValueError, "Cannot save %s images as PCX" % im.mode + raise ValueError("Cannot save %s images as PCX" % im.mode) if check: return check # bytes per plane - stride = (im.size[0] * bits + 7) / 8 + stride = (im.size[0] * bits + 7) // 8 # under windows, we could determine the current screen size with # "Image.core.display_mode()[1]", but I think that's overkill... @@ -136,11 +136,11 @@ def _save(im, fp, filename, check=0): # PCX header fp.write( - chr(10) + chr(version) + chr(1) + chr(bits) + o16(0) + + o8(10) + o8(version) + o8(1) + o8(bits) + o16(0) + o16(0) + o16(im.size[0]-1) + o16(im.size[1]-1) + o16(dpi[0]) + - o16(dpi[1]) + chr(0)*24 + chr(255)*24 + chr(0) + chr(planes) + + o16(dpi[1]) + b"\0"*24 + b"\xFF"*24 + b"\0" + o8(planes) + o16(stride) + o16(1) + o16(screen[0]) + o16(screen[1]) + - chr(0)*54 + b"\0"*54 ) assert fp.tell() == 128 @@ -150,13 +150,13 @@ def _save(im, fp, filename, check=0): if im.mode == "P": # colour palette - fp.write(chr(12)) + fp.write(o8(12)) fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes elif im.mode == "L": # greyscale palette - fp.write(chr(12)) + fp.write(o8(12)) for i in range(256): - fp.write(chr(i)*3) + fp.write(o8(i)*3) # -------------------------------------------------------------------- # registry diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index 4f957765c..462627bd0 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -22,8 +22,9 @@ __version__ = "0.4" -import Image, ImageFile -import StringIO +from . import Image, ImageFile +from ._binary import i8 +import io # @@ -60,6 +61,16 @@ def _save(im, fp, filename): xref = [0]*(5+1) # placeholders + class TextWriter: + def __init__(self, fp): + self.fp = fp + def __getattr__(self, name): + return getattr(self.fp, name) + def write(self, value): + self.fp.write(value.encode('latin-1')) + + fp = TextWriter(fp) + fp.write("%PDF-1.2\n") fp.write("% created by PIL PDF driver " + __version__ + "\n") @@ -90,11 +101,11 @@ def _save(im, fp, filename): colorspace = "[ /Indexed /DeviceRGB 255 <" palette = im.im.getpalette("RGB") for i in range(256): - r = ord(palette[i*3]) - g = ord(palette[i*3+1]) - b = ord(palette[i*3+2]) + r = i8(palette[i*3]) + g = i8(palette[i*3+1]) + b = i8(palette[i*3+2]) colorspace = colorspace + "%02x%02x%02x " % (r, g, b) - colorspace = colorspace + "> ]" + colorspace = colorspace + b"> ]" procset = "/ImageI" # indexed color elif im.mode == "RGB": filter = "/DCTDecode" @@ -127,7 +138,7 @@ def _save(im, fp, filename): # # image - op = StringIO.StringIO() + op = io.BytesIO() if filter == "/ASCIIHexDecode": if bits == 1: @@ -158,7 +169,7 @@ def _save(im, fp, filename): ColorSpace = colorspace) fp.write("stream\n") - fp.write(op.getvalue()) + fp.fp.write(op.getvalue()) fp.write("\nendstream\n") _endobj(fp) @@ -178,15 +189,15 @@ def _save(im, fp, filename): # # page contents - op = StringIO.StringIO() + op = TextWriter(io.BytesIO()) op.write("q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))) xref[5] = fp.tell() - _obj(fp, 5, Length = len(op.getvalue())) + _obj(fp, 5, Length = len(op.fp.getvalue())) fp.write("stream\n") - fp.write(op.getvalue()) + fp.fp.write(op.fp.getvalue()) fp.write("\nendstream\n") _endobj(fp) diff --git a/PIL/PixarImagePlugin.py b/PIL/PixarImagePlugin.py index d4c597b61..9322faaec 100644 --- a/PIL/PixarImagePlugin.py +++ b/PIL/PixarImagePlugin.py @@ -21,16 +21,13 @@ __version__ = "0.1" -import Image, ImageFile +from . import Image, ImageFile, _binary # # helpers -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i16 = _binary.i16le +i32 = _binary.i32le ## # Image plugin for PIXAR raster images. @@ -44,8 +41,8 @@ class PixarImageFile(ImageFile.ImageFile): # assuming a 4-byte magic label (FIXME: add "_accept" hook) s = self.fp.read(4) - if s != "\200\350\000\000": - raise SyntaxError, "not a PIXAR file" + if s != b"\200\350\000\000": + raise SyntaxError("not a PIXAR file") # read rest of header s = s + self.fp.read(508) diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 0ee8589a5..12d9fe018 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -31,22 +31,23 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + __version__ = "0.9" -import re, string +import re -import Image, ImageFile, ImagePalette, zlib +from . import Image, ImageFile, ImagePalette, _binary +import zlib + +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be + +is_cid = re.compile(b"\w\w\w\w").match -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) - -is_cid = re.compile("\w\w\w\w").match - - -_MAGIC = "\211PNG\r\n\032\n" +_MAGIC = b"\211PNG\r\n\032\n" _MODES = { @@ -96,7 +97,7 @@ class ChunkStream: len = i32(s) if not is_cid(cid): - raise SyntaxError, "broken PNG file (chunk %s)" % repr(cid) + raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) return cid, pos, len @@ -111,8 +112,8 @@ class ChunkStream: "Call the appropriate chunk handler" if Image.DEBUG: - print "STREAM", cid, pos, len - return getattr(self, "chunk_" + cid)(pos, len) + print("STREAM", cid, pos, len) + return getattr(self, "chunk_" + cid.decode('ascii'))(pos, len) def crc(self, cid, data): "Read and verify checksum" @@ -120,22 +121,22 @@ class ChunkStream: crc1 = Image.core.crc32(data, Image.core.crc32(cid)) crc2 = i16(self.fp.read(2)), i16(self.fp.read(2)) if crc1 != crc2: - raise SyntaxError, "broken PNG file"\ - "(bad header checksum in %s)" % cid + raise SyntaxError("broken PNG file"\ + "(bad header checksum in %s)" % cid) def crc_skip(self, cid, data): "Read checksum. Used if the C module is not present" self.fp.read(4) - def verify(self, endchunk = "IEND"): + def verify(self, endchunk = b"IEND"): # Simple approach; just calculate checksum for all remaining # blocks. Must be called directly after open. cids = [] - while 1: + while True: cid, pos, len = self.read() if cid == endchunk: break @@ -157,11 +158,18 @@ class PngInfo: self.chunks.append((cid, data)) def add_text(self, key, value, zip=0): + # The tEXt chunk stores latin-1 text + if not isinstance(key, bytes): + key = key.encode('latin-1', 'strict') + + if not isinstance(value, bytes): + value = value.encode('latin-1', 'replace') + if zip: import zlib - self.add("zTXt", key + "\0\0" + zlib.compress(value)) + self.add(b"zTXt", key + b"\0\0" + zlib.compress(value)) else: - self.add("tEXt", key + "\0" + value) + self.add(b"tEXt", key + b"\0" + value) # -------------------------------------------------------------------- # PNG image stream (IHDR/IEND) @@ -189,11 +197,11 @@ class PngStream(ChunkStream): # Null separator 1 byte (null character) # Compression method 1 byte (0) # Compressed profile n bytes (zlib with deflate compression) - i = string.find(s, chr(0)) + i = s.find(b"\0") if Image.DEBUG: - print "iCCP profile name", s[:i] - print "Compression method", ord(s[i]) - comp_method = ord(s[i]) + print("iCCP profile name", s[:i]) + print("Compression method", i8(s[i])) + comp_method = i8(s[i]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method) try: @@ -209,13 +217,13 @@ class PngStream(ChunkStream): s = ImageFile._safe_read(self.fp, len) self.im_size = i32(s), i32(s[4:]) try: - self.im_mode, self.im_rawmode = _MODES[(ord(s[8]), ord(s[9]))] + self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))] except: pass - if ord(s[12]): + if i8(s[12]): self.im_info["interlace"] = 1 - if ord(s[11]): - raise SyntaxError, "unknown filter category" + if i8(s[11]): + raise SyntaxError("unknown filter category") return s def chunk_IDAT(self, pos, len): @@ -243,7 +251,7 @@ class PngStream(ChunkStream): # transparency s = ImageFile._safe_read(self.fp, len) if self.im_mode == "P": - i = string.find(s, chr(0)) + i = s.find(b"\0") if i >= 0: self.im_info["transparency"] = i elif self.im_mode == "L": @@ -264,7 +272,7 @@ class PngStream(ChunkStream): # pixels per unit s = ImageFile._safe_read(self.fp, len) px, py = i32(s), i32(s[4:]) - unit = ord(s[8]) + unit = i8(s[8]) if unit == 1: # meter dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5) self.im_info["dpi"] = dpi @@ -277,10 +285,14 @@ class PngStream(ChunkStream): # text s = ImageFile._safe_read(self.fp, len) try: - k, v = string.split(s, "\0", 1) + k, v = s.split(b"\0", 1) except ValueError: - k = s; v = "" # fallback for broken tEXt tags + k = s; v = b"" # fallback for broken tEXt tags if k: + if bytes is not str: + k = k.decode('latin-1', 'strict') + v = v.decode('latin-1', 'replace') + self.im_info[k] = self.im_text[k] = v return s @@ -288,12 +300,18 @@ class PngStream(ChunkStream): # compressed text s = ImageFile._safe_read(self.fp, len) - k, v = string.split(s, "\0", 1) - comp_method = ord(v[0]) + k, v = s.split(b"\0", 1) + comp_method = i8(v[0]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) import zlib - self.im_info[k] = self.im_text[k] = zlib.decompress(v[1:]) + v = zlib.decompress(v[1:]) + + if bytes is not str: + k = k.decode('latin-1', 'strict') + v = v.decode('latin-1', 'replace') + + self.im_info[k] = self.im_text[k] = v return s # -------------------------------------------------------------------- @@ -313,14 +331,14 @@ class PngImageFile(ImageFile.ImageFile): def _open(self): if self.fp.read(8) != _MAGIC: - raise SyntaxError, "not a PNG file" + raise SyntaxError("not a PNG file") # # Parse headers up to the first IDAT chunk self.png = PngStream(self.fp) - while 1: + while True: # # get next chunk @@ -333,7 +351,7 @@ class PngImageFile(ImageFile.ImageFile): break except AttributeError: if Image.DEBUG: - print cid, pos, len, "(unknown)" + print(cid, pos, len, "(unknown)") s = ImageFile._safe_read(self.fp, len) self.png.crc(cid, s) @@ -390,9 +408,9 @@ class PngImageFile(ImageFile.ImageFile): cid, pos, len = self.png.read() - if cid not in ["IDAT", "DDAT"]: + if cid not in [b"IDAT", b"DDAT"]: self.png.push(cid, pos, len) - return "" + return b"" self.__idat = len # empty chunks are allowed @@ -417,33 +435,31 @@ class PngImageFile(ImageFile.ImageFile): # -------------------------------------------------------------------- # PNG writer -def o16(i): - return chr(i>>8&255) + chr(i&255) - -def o32(i): - return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255) +o8 = _binary.o8 +o16 = _binary.o16be +o32 = _binary.o32be _OUTMODES = { # supported PIL modes, and corresponding rawmodes/bits/color combinations - "1": ("1", chr(1)+chr(0)), - "L;1": ("L;1", chr(1)+chr(0)), - "L;2": ("L;2", chr(2)+chr(0)), - "L;4": ("L;4", chr(4)+chr(0)), - "L": ("L", chr(8)+chr(0)), - "LA": ("LA", chr(8)+chr(4)), - "I": ("I;16B", chr(16)+chr(0)), - "P;1": ("P;1", chr(1)+chr(3)), - "P;2": ("P;2", chr(2)+chr(3)), - "P;4": ("P;4", chr(4)+chr(3)), - "P": ("P", chr(8)+chr(3)), - "RGB": ("RGB", chr(8)+chr(2)), - "RGBA":("RGBA", chr(8)+chr(6)), + "1": ("1", b'\x01\x00'), + "L;1": ("L;1", b'\x01\x00'), + "L;2": ("L;2", b'\x02\x00'), + "L;4": ("L;4", b'\x04\x00'), + "L": ("L", b'\x08\x00'), + "LA": ("LA", b'\x08\x04'), + "I": ("I;16B", b'\x10\x00'), + "P;1": ("P;1", b'\x01\x03'), + "P;2": ("P;2", b'\x02\x03'), + "P;4": ("P;4", b'\x04\x03'), + "P": ("P", b'\x08\x03'), + "RGB": ("RGB", b'\x08\x02'), + "RGBA":("RGBA", b'\x08\x06'), } def putchunk(fp, cid, *data): "Write a PNG chunk (including CRC field)" - data = string.join(data, "") + data = b"".join(data) fp.write(o32(len(data)) + cid) fp.write(data) @@ -457,7 +473,7 @@ class _idat: self.fp = fp self.chunk = chunk def write(self, data): - self.chunk(self.fp, "IDAT", data) + self.chunk(self.fp, b"IDAT", data) def _save(im, fp, filename, chunk=putchunk, check=0): # save an image to disk (called by the save method) @@ -469,7 +485,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): # # attempt to minimize storage requirements for palette images - if im.encoderinfo.has_key("bits"): + if "bits" in im.encoderinfo: # number of bits specified by user n = 1 << im.encoderinfo["bits"] @@ -492,18 +508,18 @@ def _save(im, fp, filename, chunk=putchunk, check=0): mode = "%s;%d" % (mode, bits) # encoder options - if im.encoderinfo.has_key("dictionary"): + if "dictionary" in im.encoderinfo: dictionary = im.encoderinfo["dictionary"] else: - dictionary = "" + dictionary = b"" - im.encoderconfig = (im.encoderinfo.has_key("optimize"), dictionary) + im.encoderconfig = ("optimize" in im.encoderinfo, dictionary) # get the corresponding PNG mode try: rawmode, mode = _OUTMODES[mode] except KeyError: - raise IOError, "cannot write mode %s as PNG" % mode + raise IOError("cannot write mode %s as PNG" % mode) if check: return check @@ -513,39 +529,39 @@ def _save(im, fp, filename, chunk=putchunk, check=0): fp.write(_MAGIC) - chunk(fp, "IHDR", + chunk(fp, b"IHDR", o32(im.size[0]), o32(im.size[1]), # 0: size mode, # 8: depth/type - chr(0), # 10: compression - chr(0), # 11: filter category - chr(0)) # 12: interlace flag + b'\0', # 10: compression + b'\0', # 11: filter category + b'\0') # 12: interlace flag if im.mode == "P": - chunk(fp, "PLTE", im.im.getpalette("RGB")) + chunk(fp, b"PLTE", im.im.getpalette("RGB")) - if im.encoderinfo.has_key("transparency"): + if "transparency" in im.encoderinfo: if im.mode == "P": transparency = max(0, min(255, im.encoderinfo["transparency"])) - chunk(fp, "tRNS", chr(255) * transparency + chr(0)) + chunk(fp, b"tRNS", b'\xFF' * transparency + b'\0') elif im.mode == "L": transparency = max(0, min(65535, im.encoderinfo["transparency"])) - chunk(fp, "tRNS", o16(transparency)) + chunk(fp, b"tRNS", o16(transparency)) elif im.mode == "RGB": red, green, blue = im.encoderinfo["transparency"] - chunk(fp, "tRNS", o16(red) + o16(green) + o16(blue)) + chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue)) else: raise IOError("cannot use transparency for this mode") if 0: # FIXME: to be supported some day - chunk(fp, "gAMA", o32(int(gamma * 100000.0))) + chunk(fp, b"gAMA", o32(int(gamma * 100000.0))) dpi = im.encoderinfo.get("dpi") if dpi: - chunk(fp, "pHYs", + chunk(fp, b"pHYs", o32(int(dpi[0] / 0.0254 + 0.5)), o32(int(dpi[1] / 0.0254 + 0.5)), - chr(1)) + b'\x01') info = im.encoderinfo.get("pnginfo") if info: @@ -553,7 +569,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): chunk(fp, cid, data) # ICC profile writing support -- 2008-06-06 Florian Hoech - if im.info.has_key("icc_profile"): + if "icc_profile" in im.info: # ICC profile # according to PNG spec, the iCCP chunk contains: # Profile name 1-79 bytes (character string) @@ -565,13 +581,13 @@ def _save(im, fp, filename, chunk=putchunk, check=0): p = ICCProfile.ICCProfile(im.info["icc_profile"]) name = p.tags.desc.get("ASCII", p.tags.desc.get("Unicode", p.tags.desc.get("Macintosh", p.tags.desc.get("en", {}).get("US", "ICC Profile")))).encode("latin1", "replace")[:79] except ImportError: - name = "ICC Profile" - data = name + "\0\0" + zlib.compress(im.info["icc_profile"]) - chunk(fp, "iCCP", data) + name = b"ICC Profile" + data = name + b"\0\0" + zlib.compress(im.info["icc_profile"]) + chunk(fp, b"iCCP", data) ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)]) - chunk(fp, "IEND", "") + chunk(fp, b"IEND", b"") try: fp.flush() @@ -593,7 +609,7 @@ def getchunks(im, **params): self.data.append(chunk) def append(fp, cid, *data): - data = string.join(data, "") + data = b"".join(data) hi, lo = Image.core.crc32(data, Image.core.crc32(cid)) crc = o16(hi) + o16(lo) fp.append((cid, data, crc)) diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py index e86146c10..ea4266226 100644 --- a/PIL/PpmImagePlugin.py +++ b/PIL/PpmImagePlugin.py @@ -19,26 +19,28 @@ __version__ = "0.2" import string -import Image, ImageFile +from . import Image, ImageFile # # -------------------------------------------------------------------- +b_whitespace = string.whitespace.encode() + MODES = { # standard - "P4": "1", - "P5": "L", - "P6": "RGB", + b"P4": "1", + b"P5": "L", + b"P6": "RGB", # extensions - "P0CMYK": "CMYK", + b"P0CMYK": "CMYK", # PIL extensions (for test purposes only) - "PyP": "P", - "PyRGBA": "RGBA", - "PyCMYK": "CMYK" + b"PyP": "P", + b"PyRGBA": "RGBA", + b"PyCMYK": "CMYK" } def _accept(prefix): - return prefix[0] == "P" and prefix[1] in "0456y" + return prefix[0:1] == b"P" and prefix[1] in b"0456y" ## # Image plugin for PBM, PGM, and PPM images. @@ -48,10 +50,10 @@ class PpmImageFile(ImageFile.ImageFile): format = "PPM" format_description = "Pbmplus image" - def _token(self, s = ""): - while 1: # read until next whitespace + def _token(self, s = b""): + while True: # read until next whitespace c = self.fp.read(1) - if not c or c in string.whitespace: + if not c or c in b_whitespace: break s = s + c return s @@ -60,8 +62,8 @@ class PpmImageFile(ImageFile.ImageFile): # check magic s = self.fp.read(1) - if s != "P": - raise SyntaxError, "not a PPM file" + if s != b"P": + raise SyntaxError("not a PPM file") mode = MODES[self._token(s)] if mode == "1": @@ -71,12 +73,12 @@ class PpmImageFile(ImageFile.ImageFile): self.mode = rawmode = mode for ix in range(3): - while 1: - while 1: + while True: + while True: s = self.fp.read(1) - if s not in string.whitespace: + if s not in b_whitespace: break - if s != "#": + if s != b"#": break s = self.fp.readline() s = int(self._token(s)) @@ -103,18 +105,18 @@ class PpmImageFile(ImageFile.ImageFile): def _save(im, fp, filename): if im.mode == "1": - rawmode, head = "1;I", "P4" + rawmode, head = "1;I", b"P4" elif im.mode == "L": - rawmode, head = "L", "P5" + rawmode, head = "L", b"P5" elif im.mode == "RGB": - rawmode, head = "RGB", "P6" + rawmode, head = "RGB", b"P6" elif im.mode == "RGBA": - rawmode, head = "RGB", "P6" + rawmode, head = "RGB", b"P6" else: - raise IOError, "cannot write mode %s as PPM" % im.mode - fp.write(head + "\n%d %d\n" % im.size) - if head != "P4": - fp.write("255\n") + raise IOError("cannot write mode %s as PPM" % im.mode) + fp.write(head + ("\n%d %d\n" % im.size).encode('ascii')) + if head != b"P4": + fp.write(b"255\n") ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, 1))]) # ALTERNATIVE: save via builtin debug function diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index 4390f5f9c..e92aeeab0 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -18,7 +18,7 @@ __version__ = "0.4" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary MODES = { # (photoshop mode, bits) -> (pil mode, required channels) @@ -36,17 +36,15 @@ MODES = { # # helpers -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be # --------------------------------------------------------------------. # read PSD images def _accept(prefix): - return prefix[:4] == "8BPS" + return prefix[:4] == b"8BPS" ## # Image plugin for Photoshop images. @@ -64,8 +62,8 @@ class PsdImageFile(ImageFile.ImageFile): # header s = read(26) - if s[:4] != "8BPS" or i16(s[4:]) != 1: - raise SyntaxError, "not a PSD file" + if s[:4] != b"8BPS" or i16(s[4:]) != 1: + raise SyntaxError("not a PSD file") psd_bits = i16(s[22:]) psd_channels = i16(s[12:]) @@ -74,7 +72,7 @@ class PsdImageFile(ImageFile.ImageFile): mode, channels = MODES[(psd_mode, psd_bits)] if channels > psd_channels: - raise IOError, "not enough channels" + raise IOError("not enough channels") self.mode = mode self.size = i32(s[18:]), i32(s[14:]) @@ -100,7 +98,7 @@ class PsdImageFile(ImageFile.ImageFile): while self.fp.tell() < end: signature = read(4) id = i16(read(2)) - name = read(ord(read(1))) + name = read(i8(read(1))) if not (len(name) & 1): read(1) # padding data = read(i32(read(4))) @@ -146,7 +144,7 @@ class PsdImageFile(ImageFile.ImageFile): self.fp = self._fp return name, bbox except IndexError: - raise EOFError, "no such layer" + raise EOFError("no such layer") def tell(self): # return layer number (0=image, 1..max=layers) @@ -174,7 +172,7 @@ def _layerinfo(file): # image info info = [] mode = [] - types = range(i16(read(2))) + types = list(range(i16(read(2)))) if len(types) > 4: continue @@ -219,9 +217,10 @@ def _layerinfo(file): file.seek(length, 1) combined += length + 4 - length = ord(read(1)) + length = i8(read(1)) if length: - name = read(length) + # Don't know the proper encoding, Latin-1 should be a good guess + name = read(length).decode('latin-1', 'replace') combined += length + 1 file.seek(size - combined, 1) diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py index c65b91aca..ad8c3e847 100644 --- a/PIL/SgiImagePlugin.py +++ b/PIL/SgiImagePlugin.py @@ -21,14 +21,11 @@ __version__ = "0.2" -import Image, ImageFile +from . import Image, ImageFile, _binary - -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be def _accept(prefix): @@ -50,10 +47,10 @@ class SgiImageFile(ImageFile.ImageFile): raise SyntaxError("not an SGI image file") # relevant header entries - compression = ord(s[2]) + compression = i8(s[2]) # bytes, dimension, zsize - layout = ord(s[3]), i16(s[4:]), i16(s[10:]) + layout = i8(s[3]), i16(s[4:]), i16(s[10:]) # determine mode from bytes/zsize if layout == (1, 2, 1) or layout == (1, 1, 1): diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index e73d55918..2b3f4fc20 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -33,7 +33,9 @@ # http://www.wadsworth.org/spider_doc/spider/docs/image_doc.html # -import Image, ImageFile +from __future__ import print_function + +from . import Image, ImageFile import os, struct, sys def isInt(f): @@ -101,14 +103,14 @@ class SpiderImageFile(ImageFile.ImageFile): t = struct.unpack('<27f',f) # little-endian hdrlen = isSpiderHeader(t) if hdrlen == 0: - raise SyntaxError, "not a valid Spider file" + raise SyntaxError("not a valid Spider file") except struct.error: - raise SyntaxError, "not a valid Spider file" + raise SyntaxError("not a valid Spider file") h = (99,) + t # add 1 value : spider header index starts at 1 iform = int(h[5]) if iform != 1: - raise SyntaxError, "not a Spider 2D image" + raise SyntaxError("not a Spider 2D image") self.size = int(h[12]), int(h[2]) # size in pixels (width, height) self.istack = int(h[24]) @@ -131,7 +133,7 @@ class SpiderImageFile(ImageFile.ImageFile): offset = hdrlen + self.stkoffset self.istack = 2 # So Image knows it's still a stack else: - raise SyntaxError, "inconsistent stack header values" + raise SyntaxError("inconsistent stack header values") if self.bigendian: self.rawmode = "F;32BF" @@ -154,7 +156,7 @@ class SpiderImageFile(ImageFile.ImageFile): if self.istack == 0: return if frame >= self.nimages: - raise EOFError, "attempt to seek past end of file" + raise EOFError("attempt to seek past end of file") self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) self.fp = self.__fp self.fp.seek(self.stkoffset) @@ -171,7 +173,7 @@ class SpiderImageFile(ImageFile.ImageFile): # returns a ImageTk.PhotoImage object, after rescaling to 0..255 def tkPhotoImage(self): - import ImageTk + from . import ImageTk return ImageTk.PhotoImage(self.convert2byte(), palette=256) # -------------------------------------------------------------------- @@ -186,13 +188,13 @@ def loadImageSeries(filelist=None): imglist = [] for img in filelist: if not os.path.exists(img): - print "unable to find %s" % img + print("unable to find %s" % img) continue try: im = Image.open(img).convert2byte() except: if not isSpiderImage(img): - print img + " is not a Spider image file" + print(img + " is not a Spider image file") continue im.info['filename'] = img imglist.append(im) @@ -239,13 +241,13 @@ def _save(im, fp, filename): hdr = makeSpiderHeader(im) if len(hdr) < 256: - raise IOError, "Error creating Spider header" + raise IOError("Error creating Spider header") # write the SPIDER header try: fp = open(filename, 'wb') except: - raise IOError, "Unable to open %s for writing" % filename + raise IOError("Unable to open %s for writing" % filename) fp.writelines(hdr) rawmode = "F;32NF" #32-bit native floating point @@ -267,12 +269,12 @@ Image.register_save("SPIDER", _save_spider) if __name__ == "__main__": if not sys.argv[1:]: - print "Syntax: python SpiderImagePlugin.py Spiderimage [outfile]" + print("Syntax: python SpiderImagePlugin.py Spiderimage [outfile]") sys.exit() filename = sys.argv[1] if not isSpiderImage(filename): - print "input image must be in Spider format" + print("input image must be in Spider format") sys.exit() outfile = "" @@ -280,15 +282,15 @@ if __name__ == "__main__": outfile = sys.argv[2] im = Image.open(filename) - print "image: " + str(im) - print "format: " + str(im.format) - print "size: " + str(im.size) - print "mode: " + str(im.mode) - print "max, min: ", - print im.getextrema() + print("image: " + str(im)) + print("format: " + str(im.format)) + print("size: " + str(im.size)) + print("mode: " + str(im.mode)) + print("max, min: ", end=' ') + print(im.getextrema()) if outfile != "": # perform some image operation im = im.transpose(Image.FLIP_LEFT_RIGHT) - print "saving a flipped version of %s as %s " % (os.path.basename(filename), outfile) + print("saving a flipped version of %s as %s " % (os.path.basename(filename), outfile)) im.save(outfile, "SPIDER") diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py index d189562a4..9a0ca5a01 100644 --- a/PIL/SunImagePlugin.py +++ b/PIL/SunImagePlugin.py @@ -20,14 +20,10 @@ __version__ = "0.3" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary - -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i16 = _binary.i16be +i32 = _binary.i32be def _accept(prefix): @@ -46,7 +42,7 @@ class SunImageFile(ImageFile.ImageFile): # HEAD s = self.fp.read(32) if i32(s) != 0x59a66a95: - raise SyntaxError, "not an SUN raster file" + raise SyntaxError("not an SUN raster file") offset = 32 @@ -60,7 +56,7 @@ class SunImageFile(ImageFile.ImageFile): elif depth == 24: self.mode, rawmode = "RGB", "BGR" else: - raise SyntaxError, "unsupported mode" + raise SyntaxError("unsupported mode") compression = i32(s[20:24]) @@ -71,7 +67,7 @@ class SunImageFile(ImageFile.ImageFile): if self.mode == "L": self.mode = rawmode = "P" - stride = (((self.size[0] * depth + 7) / 8) + 3) & (~3) + stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3) if compression == 1: self.tile = [("raw", (0,0)+self.size, offset, (rawmode, stride))] diff --git a/PIL/TarIO.py b/PIL/TarIO.py index d5de729cd..37518e286 100644 --- a/PIL/TarIO.py +++ b/PIL/TarIO.py @@ -14,8 +14,7 @@ # See the README file for information on usage and redistribution. # -import ContainerIO -import string +from . import ContainerIO ## # A file object that provides read access to a given member of a TAR @@ -33,20 +32,20 @@ class TarIO(ContainerIO.ContainerIO): fh = open(tarfile, "rb") - while 1: + while True: s = fh.read(512) if len(s) != 512: - raise IOError, "unexpected end of tar file" + raise IOError("unexpected end of tar file") - name = s[:100] - i = string.find(name, chr(0)) + name = s[:100].decode('utf-8') + i = name.find('\0') if i == 0: - raise IOError, "cannot find subfile" + raise IOError("cannot find subfile") if i > 0: name = name[:i] - size = string.atoi(s[124:136], 8) + size = int(s[124:135], 8) if file == name: break diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py index 3375991ca..b30d8dcb0 100644 --- a/PIL/TgaImagePlugin.py +++ b/PIL/TgaImagePlugin.py @@ -19,18 +19,16 @@ __version__ = "0.3" -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary # # -------------------------------------------------------------------- # Read RGA file -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le MODES = { @@ -45,7 +43,7 @@ MODES = { def _accept(prefix): - return prefix[0] == "\0" + return prefix[0:1] == b"\0" ## # Image plugin for Targa files. @@ -60,14 +58,14 @@ class TgaImageFile(ImageFile.ImageFile): # process header s = self.fp.read(18) - id = ord(s[0]) + id = i8(s[0]) - colormaptype = ord(s[1]) - imagetype = ord(s[2]) + colormaptype = i8(s[1]) + imagetype = i8(s[2]) - depth = ord(s[16]) + depth = i8(s[16]) - flags = ord(s[17]) + flags = i8(s[17]) self.size = i16(s[12:]), i16(s[14:]) @@ -75,7 +73,7 @@ class TgaImageFile(ImageFile.ImageFile): if id != 0 or colormaptype not in (0, 1) or\ self.size[0] <= 0 or self.size[1] <= 0 or\ depth not in (1, 8, 16, 24, 32): - raise SyntaxError, "not a TGA file" + raise SyntaxError("not a TGA file") # image mode if imagetype in (3, 11): @@ -89,7 +87,7 @@ class TgaImageFile(ImageFile.ImageFile): if depth == 32: self.mode = "RGBA" else: - raise SyntaxError, "unknown TGA mode" + raise SyntaxError("unknown TGA mode") # orientation orientation = flags & 0x30 @@ -98,7 +96,7 @@ class TgaImageFile(ImageFile.ImageFile): elif not orientation: orientation = -1 else: - raise SyntaxError, "unknown TGA orientation" + raise SyntaxError("unknown TGA orientation") self.info["orientation"] = orientation @@ -110,13 +108,13 @@ class TgaImageFile(ImageFile.ImageFile): start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:]) if mapdepth == 16: self.palette = ImagePalette.raw("BGR;16", - "\0"*2*start + self.fp.read(2*size)) + b"\0"*2*start + self.fp.read(2*size)) elif mapdepth == 24: self.palette = ImagePalette.raw("BGR", - "\0"*3*start + self.fp.read(3*size)) + b"\0"*3*start + self.fp.read(3*size)) elif mapdepth == 32: self.palette = ImagePalette.raw("BGRA", - "\0"*4*start + self.fp.read(4*size)) + b"\0"*4*start + self.fp.read(4*size)) # setup tile descriptor try: @@ -135,11 +133,9 @@ class TgaImageFile(ImageFile.ImageFile): # -------------------------------------------------------------------- # Write TGA file -def o16(i): - return chr(i&255) + chr(i>>8&255) - -def o32(i): - return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) +o8 = _binary.o8 +o16 = _binary.o16le +o32 = _binary.o32le SAVE = { "1": ("1", 1, 0, 3), @@ -173,18 +169,18 @@ def _save(im, fp, filename, check=0): if orientation > 0: flags = flags | 0x20 - fp.write("\000" + - chr(colormaptype) + - chr(imagetype) + + fp.write(b"\000" + + o8(colormaptype) + + o8(imagetype) + o16(colormapfirst) + o16(colormaplength) + - chr(colormapentry) + + o8(colormapentry) + o16(0) + o16(0) + o16(im.size[0]) + o16(im.size[1]) + - chr(bits) + - chr(flags)) + o8(bits) + + o8(flags)) if colormaptype: fp.write(im.im.getpalette("RGB", "BGR")) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 8a0fde9cd..84ce1817a 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -39,48 +39,42 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + __version__ = "1.3.5" -import Image, ImageFile -import ImagePalette +from . import Image, ImageFile +from . import ImagePalette +from . import _binary -import array, string, sys +import array, sys +import collections +import itertools -II = "II" # little-endian (intel-style) -MM = "MM" # big-endian (motorola-style) +II = b"II" # little-endian (intel-style) +MM = b"MM" # big-endian (motorola-style) -try: - if sys.byteorder == "little": - native_prefix = II - else: - native_prefix = MM -except AttributeError: - if ord(array.array("i",[1]).tostring()[0]): - native_prefix = II - else: - native_prefix = MM +i8 = _binary.i8 +o8 = _binary.o8 + +if sys.byteorder == "little": + native_prefix = II +else: + native_prefix = MM # # -------------------------------------------------------------------- # Read TIFF files -def il16(c,o=0): - return ord(c[o]) + (ord(c[o+1])<<8) -def il32(c,o=0): - return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24) -def ol16(i): - return chr(i&255) + chr(i>>8&255) -def ol32(i): - return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) +il16 = _binary.i16le +il32 = _binary.i32le +ol16 = _binary.o16le +ol32 = _binary.o32le -def ib16(c,o=0): - return ord(c[o+1]) + (ord(c[o])<<8) -def ib32(c,o=0): - return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24) -def ob16(i): - return chr(i>>8&255) + chr(i&255) -def ob32(i): - return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255) +ib16 = _binary.i16be +ib32 = _binary.i32be +ob16 = _binary.o16be +ob32 = _binary.o32be # a few tag names, just to make the code below a bit more readable IMAGEWIDTH = 256 @@ -196,7 +190,7 @@ OPEN_INFO = { } -PREFIXES = ["MM\000\052", "II\052\000", "II\xBC\000"] +PREFIXES = [b"MM\000\052", b"II\052\000", b"II\xBC\000"] def _accept(prefix): return prefix[:4] in PREFIXES @@ -204,7 +198,7 @@ def _accept(prefix): ## # Wrapper for TIFF IFDs. -class ImageFileDirectory: +class ImageFileDirectory(collections.MutableMapping): # represents a TIFF tag directory. to speed things up, # we don't decode tags unless they're asked for. @@ -227,16 +221,7 @@ class ImageFileDirectory: self.tagtype = {} # added 2008-06-05 by Florian Hoech self.next = None - # dictionary API (sort of) - - def keys(self): - return self.tagdata.keys() + self.tags.keys() - - def items(self): - items = self.tags.items() - for tag in self.tagdata.keys(): - items.append((tag, self[tag])) - return items + # dictionary API def __len__(self): return len(self.tagdata) + len(self.tags) @@ -251,12 +236,6 @@ class ImageFileDirectory: del self.tagdata[tag] return data - def get(self, tag, default=None): - try: - return self[tag] - except KeyError: - return default - def getscalar(self, tag, default=None): try: value = self[tag] @@ -265,36 +244,43 @@ class ImageFileDirectory: # work around broken (?) matrox library # (from Ted Wright, via Bob Klimek) raise KeyError # use default - raise ValueError, "not a scalar" + raise ValueError("not a scalar") return value[0] except KeyError: if default is None: raise return default - def has_key(self, tag): - return self.tags.has_key(tag) or self.tagdata.has_key(tag) + def __contains__(self, tag): + return tag in self.tags or tag in self.tagdata + + if bytes is str: + def has_key(self, tag): + return tag in self def __setitem__(self, tag, value): - if type(value) is not type(()): + if not isinstance(value, tuple): value = (value,) self.tags[tag] = value + def __delitem__(self, tag): + self.tags.pop(tag, self.tagdata.pop(tag, None)) + + def __iter__(self): + return itertools.chain(self.tags.__iter__(), self.tagdata.__iter__()) + # load primitives load_dispatch = {} def load_byte(self, data): - l = [] - for i in range(len(data)): - l.append(ord(data[i])) - return tuple(l) + return data load_dispatch[1] = (1, load_byte) def load_string(self, data): - if data[-1:] == '\0': + if data[-1:] == b'\0': data = data[:-1] - return data + return data.decode('latin-1', 'replace') load_dispatch[2] = (1, load_string) def load_short(self, data): @@ -352,17 +338,17 @@ class ImageFileDirectory: tag, typ = i16(ifd), i16(ifd, 2) if Image.DEBUG: - import TiffTags + from . import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") - print "tag: %s (%d)" % (tagname, tag), - print "- type: %s (%d)" % (typname, typ), + print("tag: %s (%d)" % (tagname, tag), end=' ') + print("- type: %s (%d)" % (typname, typ), end=' ') try: dispatch = self.load_dispatch[typ] except KeyError: if Image.DEBUG: - print "- unsupported type", typ + print("- unsupported type", typ) continue # ignore unsupported type size, handler = dispatch @@ -379,16 +365,16 @@ class ImageFileDirectory: data = ifd[8:8+size] if len(data) != size: - raise IOError, "not enough data" + raise IOError("not enough data") self.tagdata[tag] = typ, data self.tagtype[tag] = typ if Image.DEBUG: if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP): - print "- value: " % size + print("- value: " % size) else: - print "- value:", self[tag] + print("- value:", self[tag]) self.next = i32(fp.read(4)) @@ -402,8 +388,7 @@ class ImageFileDirectory: fp.write(o16(len(self.tags))) # always write in ascending tag order - tags = self.tags.items() - tags.sort() + tags = sorted(self.tags.items()) directory = [] append = directory.append @@ -417,19 +402,19 @@ class ImageFileDirectory: typ = None - if self.tagtype.has_key(tag): + if tag in self.tagtype: typ = self.tagtype[tag] if typ == 1: # byte data - data = value = string.join(map(chr, value), "") + data = value elif typ == 7: # untyped data - data = value = string.join(value, "") - elif type(value[0]) is type(""): + data = value = b"".join(value) + elif isinstance(value[0], str): # string data typ = 2 - data = value = string.join(value, "\0") + "\0" + data = value = b"\0".join(value.encode('ascii', 'replace')) + b"\0" else: # integer data if tag == STRIPOFFSETS: @@ -444,31 +429,31 @@ class ImageFileDirectory: if v >= 65536: typ = 4 if typ == 3: - data = string.join(map(o16, value), "") + data = b"".join(map(o16, value)) else: - data = string.join(map(o32, value), "") + data = b"".join(map(o32, value)) if Image.DEBUG: - import TiffTags + from . import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") - print "save: %s (%d)" % (tagname, tag), - print "- type: %s (%d)" % (typname, typ), + print("save: %s (%d)" % (tagname, tag), end=' ') + print("- type: %s (%d)" % (typname, typ), end=' ') if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP): size = len(data) - print "- value: " % size + print("- value: " % size) else: - print "- value:", value + print("- value:", value) # figure out if data fits into the directory if len(data) == 4: - append((tag, typ, len(value), data, "")) + append((tag, typ, len(value), data, b"")) elif len(data) < 4: - append((tag, typ, len(value), data + (4-len(data))*"\0", "")) + append((tag, typ, len(value), data + (4-len(data))*b"\0", b"")) else: count = len(value) if typ == 5: - count = count / 2 # adjust for rational data field + count = count // 2 # adjust for rational data field append((tag, typ, count, o32(offset), data)) offset = offset + len(data) if offset & 1: @@ -484,17 +469,17 @@ class ImageFileDirectory: # pass 2: write directory to file for tag, typ, count, value, data in directory: if Image.DEBUG > 1: - print tag, typ, count, repr(value), repr(data) + print(tag, typ, count, repr(value), repr(data)) fp.write(o16(tag) + o16(typ) + o32(count) + value) # -- overwrite here for multi-page -- - fp.write("\0\0\0\0") # end of directory + fp.write(b"\0\0\0\0") # end of directory # pass 3: write auxiliary data to file for tag, typ, count, value, data in directory: fp.write(data) if len(data) & 1: - fp.write("\0") + fp.write(b"\0") return offset @@ -513,7 +498,7 @@ class TiffImageFile(ImageFile.ImageFile): ifh = self.fp.read(8) if ifh[:4] not in PREFIXES: - raise SyntaxError, "not a TIFF file" + raise SyntaxError("not a TIFF file") # image file directory (tag dictionary) self.tag = self.ifd = ImageFileDirectory(ifh[:2]) @@ -547,7 +532,7 @@ class TiffImageFile(ImageFile.ImageFile): self.__next = self.__first while self.__frame < frame: if not self.__next: - raise EOFError, "no more images in TIFF file" + raise EOFError("no more images in TIFF file") self.fp.seek(self.__next) self.tag.load(self.fp) self.__next = self.tag.next @@ -569,18 +554,18 @@ class TiffImageFile(ImageFile.ImageFile): args = (rawmode, 0, 1) elif compression == "jpeg": args = rawmode, "" - if self.tag.has_key(JPEGTABLES): + if JPEGTABLES in self.tag: # Hack to handle abbreviated JPEG headers self.tile_prefix = self.tag[JPEGTABLES] elif compression == "packbits": args = rawmode elif compression == "tiff_lzw": args = rawmode - if self.tag.has_key(317): + if 317 in self.tag: # Section 14: Differencing Predictor self.decoderconfig = (self.tag[PREDICTOR][0],) - if self.tag.has_key(ICCPROFILE): + if ICCPROFILE in self.tag: self.info['icc_profile'] = self.tag[ICCPROFILE] return args @@ -588,8 +573,8 @@ class TiffImageFile(ImageFile.ImageFile): def _setup(self): "Setup this image object based on current tags" - if self.tag.has_key(0xBC01): - raise IOError, "Windows Media Photo files not yet supported" + if 0xBC01 in self.tag: + raise IOError("Windows Media Photo files not yet supported") getscalar = self.tag.getscalar @@ -604,11 +589,11 @@ class TiffImageFile(ImageFile.ImageFile): fillorder = getscalar(FILLORDER, 1) if Image.DEBUG: - print "*** Summary ***" - print "- compression:", self._compression - print "- photometric_interpretation:", photo - print "- planar_configuration:", self._planar_configuration - print "- fill_order:", fillorder + print("*** Summary ***") + print("- compression:", self._compression) + print("- photometric_interpretation:", photo) + print("- planar_configuration:", self._planar_configuration) + print("- fill_order:", fillorder) # size xsize = getscalar(IMAGEWIDTH) @@ -616,7 +601,7 @@ class TiffImageFile(ImageFile.ImageFile): self.size = xsize, ysize if Image.DEBUG: - print "- size:", self.size + print("- size:", self.size) format = getscalar(SAMPLEFORMAT, 1) @@ -627,17 +612,17 @@ class TiffImageFile(ImageFile.ImageFile): self.tag.get(EXTRASAMPLES, ()) ) if Image.DEBUG: - print "format key:", key + print("format key:", key) try: self.mode, rawmode = OPEN_INFO[key] except KeyError: if Image.DEBUG: - print "- unsupported format" - raise SyntaxError, "unknown pixel mode" + print("- unsupported format") + raise SyntaxError("unknown pixel mode") if Image.DEBUG: - print "- raw mode:", rawmode - print "- pil mode:", self.mode + print("- raw mode:", rawmode) + print("- pil mode:", self.mode) self.info["compression"] = self._compression @@ -658,7 +643,7 @@ class TiffImageFile(ImageFile.ImageFile): # build tile descriptors x = y = l = 0 self.tile = [] - if self.tag.has_key(STRIPOFFSETS): + if STRIPOFFSETS in self.tag: # striped image h = getscalar(ROWSPERSTRIP, ysize) w = self.size[0] @@ -675,7 +660,7 @@ class TiffImageFile(ImageFile.ImageFile): x = y = 0 l = l + 1 a = None - elif self.tag.has_key(TILEOFFSETS): + elif TILEOFFSETS in self.tag: # tiled image w = getscalar(322) h = getscalar(323) @@ -698,14 +683,14 @@ class TiffImageFile(ImageFile.ImageFile): a = None else: if Image.DEBUG: - print "- unsupported data organization" + print("- unsupported data organization") raise SyntaxError("unknown data organization") # fixup palette descriptor if self.mode == "P": - palette = map(lambda a: chr(a / 256), self.tag[COLORMAP]) - self.palette = ImagePalette.raw("RGB;L", string.join(palette, "")) + palette = [o8(a // 256) for a in self.tag[COLORMAP]] + self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) # # -------------------------------------------------------------------- # Write TIFF files @@ -738,10 +723,10 @@ SAVE_INFO = { def _cvt_res(value): # convert value to TIFF rational number -- (numerator, denominator) - if type(value) in (type([]), type(())): + if isinstance(value, collections.Sequence): assert(len(value) % 2 == 0) return value - if type(value) == type(1): + if isinstance(value, int): return (value, 1) value = float(value) return (int(value * 65536), 65536) @@ -751,7 +736,7 @@ def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] except KeyError: - raise IOError, "cannot write mode %s as TIFF" % im.mode + raise IOError("cannot write mode %s as TIFF" % im.mode) ifd = ImageFileDirectory(prefix) @@ -769,28 +754,28 @@ def _save(im, fp, filename): if hasattr(im, 'tag'): # preserve tags from original TIFF image file for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION): - if im.tag.tagdata.has_key(key): + if key in im.tag.tagdata: ifd[key] = im.tag.tagdata.get(key) # preserve some more tags from original TIFF image file # -- 2008-06-06 Florian Hoech ifd.tagtype = im.tag.tagtype for key in (IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): - if im.tag.has_key(key): + if key in im.tag: ifd[key] = im.tag[key] # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech - if im.info.has_key("icc_profile"): + if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] - if im.encoderinfo.has_key("description"): + if "description" in im.encoderinfo: ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"] - if im.encoderinfo.has_key("resolution"): + if "resolution" in im.encoderinfo: ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \ = _cvt_res(im.encoderinfo["resolution"]) - if im.encoderinfo.has_key("x resolution"): + if "x resolution" in im.encoderinfo: ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"]) - if im.encoderinfo.has_key("y resolution"): + if "y resolution" in im.encoderinfo: ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"]) - if im.encoderinfo.has_key("resolution unit"): + if "resolution unit" in im.encoderinfo: unit = im.encoderinfo["resolution unit"] if unit == "inch": ifd[RESOLUTION_UNIT] = 2 @@ -798,13 +783,13 @@ def _save(im, fp, filename): ifd[RESOLUTION_UNIT] = 3 else: ifd[RESOLUTION_UNIT] = 1 - if im.encoderinfo.has_key("software"): + if "software" in im.encoderinfo: ifd[SOFTWARE] = im.encoderinfo["software"] - if im.encoderinfo.has_key("date time"): + if "date time" in im.encoderinfo: ifd[DATE_TIME] = im.encoderinfo["date time"] - if im.encoderinfo.has_key("artist"): + if "artist" in im.encoderinfo: ifd[ARTIST] = im.encoderinfo["artist"] - if im.encoderinfo.has_key("copyright"): + if "copyright" in im.encoderinfo: ifd[COPYRIGHT] = im.encoderinfo["copyright"] dpi = im.encoderinfo.get("dpi") @@ -826,10 +811,10 @@ def _save(im, fp, filename): if im.mode == "P": lut = im.im.getpalette("RGB", "RGB;L") - ifd[COLORMAP] = tuple(map(lambda v: ord(v) * 256, lut)) + ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) # data orientation - stride = len(bits) * ((im.size[0]*bits[0]+7)/8) + stride = len(bits) * ((im.size[0]*bits[0]+7)//8) ifd[ROWSPERSTRIP] = im.size[1] ifd[STRIPBYTECOUNTS] = stride * im.size[1] ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer @@ -843,7 +828,7 @@ def _save(im, fp, filename): # -- helper for multi-page save -- - if im.encoderinfo.has_key("_debug_multipage"): + if "_debug_multipage" in im.encoderinfo: #just to access o32 and o16 (using correct byte order) im._debug_multipage = ifd diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py index fb8c38ab4..5d2b701ab 100644 --- a/PIL/WalImageFile.py +++ b/PIL/WalImageFile.py @@ -21,10 +21,17 @@ # http://www.flipcode.com/tutorials/tut_q2levels.shtml # and has been tested with a few sample files found using google. -import Image +from __future__ import print_function -def i32(c, o=0): - return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24) +from . import Image, _binary + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +i32 = _binary.i32le ## # Load texture from a Quake2 WAL texture file. @@ -42,8 +49,7 @@ def open(filename): if hasattr(filename, "read"): fp = filename else: - import __builtin__ - fp = __builtin__.open(filename, "rb") + fp = builtins.open(filename, "rb") # read header fields header = fp.read(32+24+32+12) @@ -53,15 +59,15 @@ def open(filename): # load pixel data fp.seek(offset) - im = Image.fromstring("P", size, fp.read(size[0] * size[1])) + im = Image.frombytes("P", size, fp.read(size[0] * size[1])) im.putpalette(quake2palette) im.format = "WAL" im.format_description = "Quake2 Texture" # strings are null-terminated - im.info["name"] = header[:32].split("\0", 1)[0] - next_name = header[56:56+32].split("\0", 1)[0] + im.info["name"] = header[:32].split(b"\0", 1)[0] + next_name = header[56:56+32].split(b"\0", 1)[0] if next_name: im.info["next_name"] = next_name @@ -70,57 +76,57 @@ def open(filename): quake2palette = ( # default palette taken from piffo 0.93 by Hans Häggström - "\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" - "\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" - "\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" - "\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" - "\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" - "\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" - "\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" - "\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" - "\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" - "\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" - "\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" - "\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" - "\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" - "\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" - "\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" - "\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" - "\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" - "\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" - "\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" - "\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" - "\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" - "\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" - "\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" - "\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" - "\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" - "\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" - "\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" - "\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" - "\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" - "\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" - "\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" - "\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" - "\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" - "\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" - "\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" - "\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" - "\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" - "\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" - "\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" - "\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" - "\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" - "\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" - "\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" - "\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" - "\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" - "\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" - "\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" - "\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" + b"\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" + b"\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" + b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" + b"\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" + b"\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" + b"\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" + b"\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" + b"\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" + b"\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" + b"\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" + b"\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" + b"\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" + b"\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" + b"\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" + b"\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" + b"\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" + b"\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" + b"\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" + b"\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" + b"\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" + b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" + b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" + b"\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" + b"\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" + b"\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" + b"\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" + b"\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" + b"\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" + b"\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" + b"\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" + b"\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" + b"\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" + b"\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" + b"\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" + b"\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" + b"\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" + b"\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" + b"\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" + b"\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" + b"\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" + b"\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" + b"\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" + b"\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" + b"\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" + b"\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" + b"\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" + b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" + b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" ) if __name__ == "__main__": im = open("../hacks/sample.wal") - print im.info, im.mode, im.size + print(im.info, im.mode, im.size) im.save("../out.png") diff --git a/PIL/WmfImagePlugin.py b/PIL/WmfImagePlugin.py index 5191f1e51..e77d1eb77 100644 --- a/PIL/WmfImagePlugin.py +++ b/PIL/WmfImagePlugin.py @@ -17,10 +17,13 @@ __version__ = "0.2" -import Image, ImageFile +from . import Image, ImageFile, _binary _handler = None +if str != bytes: + long = int + ## # Install application-specific WMF image handler. # @@ -41,7 +44,7 @@ if hasattr(Image.core, "drawwmf"): def load(self, im): im.fp.seek(0) # rewind - return Image.fromstring( + return Image.frombytes( "RGB", im.size, Image.core.drawwmf(im.fp.read(), im.size, self.bbox), "raw", "BGR", (im.size[0]*3 + 3) & -4, -1 @@ -51,20 +54,15 @@ if hasattr(Image.core, "drawwmf"): # -------------------------------------------------------------------- -def word(c, o=0): - return ord(c[o]) + (ord(c[o+1])<<8) +word = _binary.i16le def short(c, o=0): - v = ord(c[o]) + (ord(c[o+1])<<8) + v = word(c, o) if v >= 32768: v = v - 65536 return v -def dword(c, o=0): - return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24) - -def long(c, o=0): - return dword(c, o) +dword = _binary.i32le # # -------------------------------------------------------------------- @@ -72,8 +70,8 @@ def long(c, o=0): def _accept(prefix): return ( - prefix[:6] == "\xd7\xcd\xc6\x9a\x00\x00" or - prefix[:4] == "\x01\x00\x00\x00" + prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or + prefix[:4] == b"\x01\x00\x00\x00" ) ## @@ -89,7 +87,7 @@ class WmfStubImageFile(ImageFile.StubImageFile): # check placable header s = self.fp.read(80) - if s[:6] == "\xd7\xcd\xc6\x9a\x00\x00": + if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00": # placeable windows metafile @@ -101,7 +99,7 @@ class WmfStubImageFile(ImageFile.StubImageFile): x1 = short(s, 10); y1 = short(s, 12) # normalize size to 72 dots per inch - size = (x1 - x0) * 72 / inch, (y1 - y0) * 72 / inch + size = (x1 - x0) * 72 // inch, (y1 - y0) * 72 // inch self.info["wmf_bbox"] = x0, y0, x1, y1 @@ -110,25 +108,25 @@ class WmfStubImageFile(ImageFile.StubImageFile): # print self.mode, self.size, self.info # sanity check (standard metafile header) - if s[22:26] != "\x01\x00\t\x00": + if s[22:26] != b"\x01\x00\t\x00": raise SyntaxError("Unsupported WMF file format") - elif long(s) == 1 and s[40:44] == " EMF": + elif dword(s) == 1 and s[40:44] == b" EMF": # enhanced metafile # get bounding box - x0 = long(s, 8); y0 = long(s, 12) - x1 = long(s, 16); y1 = long(s, 20) + x0 = dword(s, 8); y0 = dword(s, 12) + x1 = dword(s, 16); y1 = dword(s, 20) # get frame (in 0.01 millimeter units) - frame = long(s, 24), long(s, 28), long(s, 32), long(s, 36) + frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36) # normalize size to 72 dots per inch size = x1 - x0, y1 - y0 # calculate dots per inch from bbox and frame - xdpi = 2540 * (x1 - y0) / (frame[2] - frame[0]) - ydpi = 2540 * (y1 - y0) / (frame[3] - frame[1]) + xdpi = 2540 * (x1 - y0) // (frame[2] - frame[0]) + ydpi = 2540 * (y1 - y0) // (frame[3] - frame[1]) self.info["wmf_bbox"] = x0, y0, x1, y1 diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py index ab1fb5d7a..e7f8c90cd 100644 --- a/PIL/XVThumbImagePlugin.py +++ b/PIL/XVThumbImagePlugin.py @@ -19,15 +19,16 @@ __version__ = "0.1" -import string -import Image, ImageFile, ImagePalette +from . import Image, ImageFile, ImagePalette, _binary + +o8 = _binary.o8 # standard color palette for thumbnails (RGB332) -PALETTE = "" +PALETTE = b"" for r in range(8): for g in range(8): for b in range(4): - PALETTE = PALETTE + (chr((r*255)/7)+chr((g*255)/7)+chr((b*255)/3)) + PALETTE = PALETTE + (o8((r*255)//7)+o8((g*255)//7)+o8((b*255)//3)) ## # Image plugin for XV thumbnail images. @@ -41,25 +42,25 @@ class XVThumbImageFile(ImageFile.ImageFile): # check magic s = self.fp.read(6) - if s != "P7 332": - raise SyntaxError, "not an XV thumbnail file" + if s != b"P7 332": + raise SyntaxError("not an XV thumbnail file") # Skip to beginning of next line self.fp.readline() # skip info comments - while 1: + while True: s = self.fp.readline() if not s: - raise SyntaxError, "Unexpected EOF reading XV thumbnail file" - if s[0] != '#': + raise SyntaxError("Unexpected EOF reading XV thumbnail file") + if s[0] != b'#': break # parse header line (already read) - s = string.split(s.strip()) + s = s.strip().split() self.mode = "P" - self.size = int(s[0]), int(s[1]) + self.size = int(s[0:1]), int(s[1:2]) self.palette = ImagePalette.raw("RGB", PALETTE) diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py index a8cf1026d..e89b83c05 100644 --- a/PIL/XbmImagePlugin.py +++ b/PIL/XbmImagePlugin.py @@ -21,22 +21,22 @@ __version__ = "0.6" -import re, string -import Image, ImageFile +import re +from . import Image, ImageFile # XBM header xbm_head = re.compile( - "\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+" - "#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+" - "(?P" - "#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" - "#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" - ")?" - "[\\000-\\377]*_bits\\[\\]" + b"\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+" + b"(?P" + b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" + b")?" + b"[\\000-\\377]*_bits\\[\\]" ) def _accept(prefix): - return string.lstrip(prefix)[:7] == "#define" + return prefix.lstrip()[:7] == b"#define" ## # Image plugin for X11 bitmaps. @@ -69,21 +69,21 @@ class XbmImageFile(ImageFile.ImageFile): def _save(im, fp, filename): if im.mode != "1": - raise IOError, "cannot write mode %s as XBM" % im.mode + raise IOError("cannot write mode %s as XBM" % im.mode) - fp.write("#define im_width %d\n" % im.size[0]) - fp.write("#define im_height %d\n" % im.size[1]) + fp.write(("#define im_width %d\n" % im.size[0]).encode('ascii')) + fp.write(("#define im_height %d\n" % im.size[1]).encode('ascii')) hotspot = im.encoderinfo.get("hotspot") if hotspot: - fp.write("#define im_x_hot %d\n" % hotspot[0]) - fp.write("#define im_y_hot %d\n" % hotspot[1]) + fp.write(("#define im_x_hot %d\n" % hotspot[0]).encode('ascii')) + fp.write(("#define im_y_hot %d\n" % hotspot[1]).encode('ascii')) - fp.write("static char im_bits[] = {\n") + fp.write(b"static char im_bits[] = {\n") ImageFile._save(im, fp, [("xbm", (0,0)+im.size, 0, None)]) - fp.write("};\n") + fp.write(b"};\n") Image.register_open("XBM", XbmImageFile, _accept) diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py index a3f40f02c..304ba33b7 100644 --- a/PIL/XpmImagePlugin.py +++ b/PIL/XpmImagePlugin.py @@ -18,15 +18,16 @@ __version__ = "0.2" -import re, string -import Image, ImageFile, ImagePalette +import re +from . import Image, ImageFile, ImagePalette +from ._binary import i8, o8 # XPM header -xpm_head = re.compile("\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)") +xpm_head = re.compile(b"\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)") def _accept(prefix): - return prefix[:9] == "/* XPM */" + return prefix[:9] == b"/* XPM */" ## # Image plugin for X11 pixel maps. @@ -39,13 +40,13 @@ class XpmImageFile(ImageFile.ImageFile): def _open(self): if not _accept(self.fp.read(9)): - raise SyntaxError, "not an XPM file" + raise SyntaxError("not an XPM file") # skip forward to next string - while 1: + while True: s = self.fp.readline() if not s: - raise SyntaxError, "broken XPM file" + raise SyntaxError("broken XPM file") m = xpm_head.match(s) if m: break @@ -56,50 +57,50 @@ class XpmImageFile(ImageFile.ImageFile): bpp = int(m.group(4)) if pal > 256 or bpp != 1: - raise ValueError, "cannot read this XPM file" + raise ValueError("cannot read this XPM file") # # load palette description - palette = ["\0\0\0"] * 256 + palette = [b"\0\0\0"] * 256 for i in range(pal): s = self.fp.readline() - if s[-2:] == '\r\n': + if s[-2:] == b'\r\n': s = s[:-2] - elif s[-1:] in '\r\n': + elif s[-1:] in b'\r\n': s = s[:-1] - c = ord(s[1]) - s = string.split(s[2:-2]) + c = i8(s[1]) + s = s[2:-2].split() for i in range(0, len(s), 2): - if s[i] == "c": + if s[i] == b"c": # process colour key rgb = s[i+1] - if rgb == "None": + if rgb == b"None": self.info["transparency"] = c - elif rgb[0] == "#": + elif rgb[0:1] == b"#": # FIXME: handle colour names (see ImagePalette.py) - rgb = string.atoi(rgb[1:], 16) - palette[c] = chr((rgb >> 16) & 255) +\ - chr((rgb >> 8) & 255) +\ - chr(rgb & 255) + rgb = int(rgb[1:], 16) + palette[c] = o8((rgb >> 16) & 255) +\ + o8((rgb >> 8) & 255) +\ + o8(rgb & 255) else: # unknown colour - raise ValueError, "cannot read this XPM file" + raise ValueError("cannot read this XPM file") break else: # missing colour key - raise ValueError, "cannot read this XPM file" + raise ValueError("cannot read this XPM file") self.mode = "P" - self.palette = ImagePalette.raw("RGB", string.join(palette, "")) + self.palette = ImagePalette.raw("RGB", b"".join(palette)) self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))] @@ -113,11 +114,11 @@ class XpmImageFile(ImageFile.ImageFile): s = [None] * ysize for i in range(ysize): - s[i] = string.ljust(self.fp.readline()[1:xsize+1], xsize) + s[i] = self.fp.readline()[1:xsize+1].ljust(xsize) self.fp = None - return string.join(s, "") + return b"".join(s) # # Registry diff --git a/PIL/_binary.py b/PIL/_binary.py new file mode 100644 index 000000000..37318a21a --- /dev/null +++ b/PIL/_binary.py @@ -0,0 +1,52 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Binary input/output support routines. +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1995-2003 by Fredrik Lundh +# Copyright (c) 2012 by Brian Crowell +# +# See the README file for information on usage and redistribution. +# + +if bytes is str: + def i8(c): + return ord(c) + + def o8(i): + return chr(i&255) +else: + def i8(c): + return c if c.__class__ is int else c[0] + + def o8(i): + return bytes((i&255,)) + +# Input, le = little endian, be = big endian +def i16le(c, o=0): + return i8(c[o]) | (i8(c[o+1])<<8) + +def i32le(c, o=0): + return i8(c[o]) | (i8(c[o+1])<<8) | (i8(c[o+2])<<16) | (i8(c[o+3])<<24) + +def i16be(c, o=0): + return (i8(c[o])<<8) | i8(c[o+1]) + +def i32be(c, o=0): + return (i8(c[o])<<24) | (i8(c[o+1])<<16) | (i8(c[o+2])<<8) | i8(c[o+3]) + +# Output, le = little endian, be = big endian +def o16le(i): + return o8(i) + o8(i>>8) + +def o32le(i): + return o8(i) + o8(i>>8) + o8(i>>16) + o8(i>>24) + +def o16be(i): + return o8(i>>8) + o8(i) + +def o32be(i): + return o8(i>>24) + o8(i>>16) + o8(i>>8) + o8(i) + diff --git a/Sane/_sane.c b/Sane/_sane.c index 21e542fa5..c94a120a0 100644 --- a/Sane/_sane.c +++ b/Sane/_sane.c @@ -28,6 +28,12 @@ PERFORMANCE OF THIS SOFTWARE. #include +#if PY_MAJOR_VERSION >= 3 + #define PyInt_AsLong PyLong_AsLong + #define PyInt_FromLong PyLong_FromLong + #define PyInt_Check PyLong_Check +#endif + static PyObject *ErrorObject; typedef struct { @@ -51,14 +57,18 @@ PySane_Error(SANE_Status st) return NULL; } -staticforward PyTypeObject SaneDev_Type; +static PyTypeObject SaneDev_Type; -#define SaneDevObject_Check(v) ((v)->ob_type == &SaneDev_Type) +#define SaneDevObject_Check(v) (Py_TYPE(v) == &SaneDev_Type) static SaneDevObject * newSaneDevObject(void) { SaneDevObject *self; + + if (PyType_Ready(&SaneDev_Type) < 0) + return NULL; + self = PyObject_NEW(SaneDevObject, &SaneDev_Type); if (self == NULL) return NULL; @@ -233,7 +243,11 @@ SaneDev_get_options(SaneDevObject *self, PyObject *args) constraint=PyList_New(0); for(j=0; d->constraint.string_list[j]!=NULL; j++) PyList_Append(constraint, +#if PY_MAJOR_VERSION >= 3 + PyUnicode_DecodeLatin1(d->constraint.string_list[j], strlen(d->constraint.string_list[j]), NULL)); +#else PyString_FromString(d->constraint.string_list[j])); +#endif break; } value=Py_BuildValue("isssiiiiO", i, d->name, d->title, d->desc, @@ -284,7 +298,11 @@ SaneDev_get_option(SaneDevObject *self, PyObject *args) value=Py_BuildValue("d", SANE_UNFIX((*((SANE_Fixed*)v))) ); break; case(SANE_TYPE_STRING): +#if PY_MAJOR_VERSION >= 3 + value=PyUnicode_DecodeLatin1((const char *) v, strlen((const char *) v), NULL); +#else value=Py_BuildValue("s", v); +#endif break; case(SANE_TYPE_BUTTON): case(SANE_TYPE_GROUP): @@ -345,7 +363,25 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args) *( (SANE_Fixed*)v) = SANE_FIX(PyFloat_AsDouble(value)); break; case(SANE_TYPE_STRING): - if (!PyString_Check(value)) +#if PY_MAJOR_VERSION >= 3 + if (!PyUnicode_Check(value)) + { + PyErr_SetString(PyExc_TypeError, "SANE_STRING requires a string"); + free(v); + return NULL; + } + { + PyObject *encoded = PyUnicode_AsLatin1String(value); + + if (!encoded) + return NULL; + + strncpy(v, PyBytes_AsString(encoded), d->size-1); + ((char*)v)[d->size-1] = 0; + Py_DECREF(encoded); + } +#else + if (!PyString_Check(value)) { PyErr_SetString(PyExc_TypeError, "SANE_STRING requires a string"); free(v); @@ -353,6 +389,7 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args) } strncpy(v, PyString_AsString(value), d->size-1); ((char*)v)[d->size-1] = 0; +#endif break; case(SANE_TYPE_BUTTON): case(SANE_TYPE_GROUP): @@ -1095,29 +1132,38 @@ static PyMethodDef SaneDev_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject * -SaneDev_getattr(SaneDevObject *self, char *name) -{ - return Py_FindMethod(SaneDev_methods, (PyObject *)self, name); -} - -staticforward PyTypeObject SaneDev_Type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, /*ob_size*/ +static PyTypeObject SaneDev_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "SaneDev", /*tp_name*/ sizeof(SaneDevObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)SaneDev_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)SaneDev_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SaneDev_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; /* --------------------------------------------------------------------- */ @@ -1152,8 +1198,8 @@ PySane_exit(PyObject *self, PyObject *args) static PyObject * PySane_get_devices(PyObject *self, PyObject *args) { - SANE_Device **devlist; - SANE_Device *dev; + const SANE_Device **devlist; + const SANE_Device *dev; SANE_Status st; PyObject *list; int local_only, i; @@ -1163,7 +1209,9 @@ PySane_get_devices(PyObject *self, PyObject *args) return NULL; } + Py_BEGIN_ALLOW_THREADS st=sane_get_devices(&devlist, local_only); + Py_END_ALLOW_THREADS if (st) return PySane_Error(st); if (!(list = PyList_New(0))) return NULL; @@ -1191,7 +1239,9 @@ PySane_open(PyObject *self, PyObject *args) rv = newSaneDevObject(); if ( rv == NULL ) return NULL; + Py_BEGIN_ALLOW_THREADS st = sane_open(name, &(rv->h)); + Py_END_ALLOW_THREADS if (st) { Py_DECREF(rv); @@ -1248,17 +1298,40 @@ insint(PyObject *d, char *name, int value) Py_DECREF(v); } -void +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef PySane_moduledef = { + PyModuleDef_HEAD_INIT, + "_sane", + NULL, + 0, + PySane_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__sane(void) +{ + /* Create the module and add the functions */ + PyObject *m = PyModule_Create(&PySane_moduledef); + if(!m) + return NULL; +#else /* if PY_MAJOR_VERSION < 3 */ + +PyMODINIT_FUNC init_sane(void) { - PyObject *m, *d; - - /* Create the module and add the functions */ - m = Py_InitModule("_sane", PySane_methods); + /* Create the module and add the functions */ + PyObject *m = Py_InitModule("_sane", PySane_methods); + if(!m) + return; +#endif /* Add some symbolic constants to the module */ - d = PyModule_GetDict(m); - ErrorObject = PyString_FromString("_sane.error"); + PyObject *d = PyModule_GetDict(m); + ErrorObject = PyErr_NewException("_sane.error", NULL, NULL); PyDict_SetItemString(d, "error", ErrorObject); insint(d, "INFO_INEXACT", SANE_INFO_INEXACT); @@ -1322,4 +1395,7 @@ init_sane(void) NUMARRAY_IMPORTED = 1; #endif /* WITH_NUMARRAY */ +#if PY_MAJOR_VERSION >= 3 + return m; +#endif } diff --git a/Sane/demo_numarray.py b/Sane/demo_numarray.py index 0104af2d5..57fcc4407 100644 --- a/Sane/demo_numarray.py +++ b/Sane/demo_numarray.py @@ -4,6 +4,8 @@ # Shows how to scan a 16 bit grayscale image into a numarray object # +from __future__ import print_function + # Get the path set up to find PIL modules if not installed yet: import sys ; sys.path.append('../PIL') @@ -14,17 +16,17 @@ import Image def toImage(arr): if arr.type().bytes == 1: # need to swap coordinates btw array and image (with [::-1]) - im = Image.fromstring('L', arr.shape[::-1], arr.tostring()) + im = Image.frombytes('L', arr.shape[::-1], arr.tostring()) else: arr_c = arr - arr.min() arr_c *= (255./arr_c.max()) arr = arr_c.astype(UInt8) # need to swap coordinates btw array and image (with [::-1]) - im = Image.fromstring('L', arr.shape[::-1], arr.tostring()) + im = Image.frombytes('L', arr.shape[::-1], arr.tostring()) return im -print 'SANE version:', sane.init() -print 'Available devices=', sane.get_devices() +print('SANE version:', sane.init()) +print('Available devices=', sane.get_devices()) s = sane.open(sane.get_devices()[0][0]) @@ -32,7 +34,7 @@ s = sane.open(sane.get_devices()[0][0]) s.mode = 'gray' s.br_x=320. ; s.br_y=240. -print 'Device parameters:', s.get_parameters() +print('Device parameters:', s.get_parameters()) s.depth=16 arr16 = s.arr_scan() diff --git a/Sane/demo_pil.py b/Sane/demo_pil.py index 016361f8a..490f33158 100644 --- a/Sane/demo_pil.py +++ b/Sane/demo_pil.py @@ -4,19 +4,21 @@ # Shows how to scan a color image into a PIL rgb-image # +from __future__ import print_function + # Get the path set up to find PIL modules if not installed yet: import sys ; sys.path.append('../PIL') import sane -print 'SANE version:', sane.init() -print 'Available devices=', sane.get_devices() +print('SANE version:', sane.init()) +print('Available devices=', sane.get_devices()) s = sane.open(sane.get_devices()[0][0]) s.mode = 'color' s.br_x=320. ; s.br_y=240. -print 'Device parameters:', s.get_parameters() +print('Device parameters:', s.get_parameters()) # Initiate the scan s.start() diff --git a/Sane/sane.py b/Sane/sane.py index 27be5a259..331776f95 100644 --- a/Sane/sane.py +++ b/Sane/sane.py @@ -46,7 +46,6 @@ class Option: """ def __init__(self, args, scanDev): - import string self.scanDev = scanDev # needed to get current value of this option self.index, self.name = args[0], args[1] self.title, self.desc = args[2], args[3] @@ -56,8 +55,8 @@ class Option: def f(x): if x=='-': return '_' else: return x - if type(self.name)!=type(''): self.py_name=str(self.name) - else: self.py_name=string.join(map(f, self.name), '') + if not isinstance(self.name, str): self.py_name=str(self.name) + else: self.py_name=''.join(map(f, self.name)) def is_active(self): return _sane.OPTION_IS_ACTIVE(self.cap) @@ -86,7 +85,7 @@ active: %s settable: %s\n""" % (self.py_name, curValue, self.index, self.title, self.desc, TYPE_STR[self.type], UNIT_STR[self.unit], - `self.constraint`, active, settable) + repr(self.constraint), active, settable) return s @@ -106,7 +105,7 @@ class _SaneIterator: def next(self): try: self.device.start() - except error, v: + except error as v: if v == 'Document feeder out of documents': raise StopIteration else: @@ -166,16 +165,16 @@ class SaneDev: def __setattr__(self, key, value): dev=self.__dict__['dev'] optdict=self.__dict__['opt'] - if not optdict.has_key(key): + if key not in optdict: self.__dict__[key]=value ; return opt=optdict[key] if opt.type==TYPE_GROUP: - raise AttributeError, "Groups can't be set: "+key + raise AttributeError("Groups can't be set: "+key) if not _sane.OPTION_IS_ACTIVE(opt.cap): - raise AttributeError, 'Inactive option: '+key + raise AttributeError('Inactive option: '+key) if not _sane.OPTION_IS_SETTABLE(opt.cap): - raise AttributeError, "Option can't be set by software: "+key - if type(value) == int and opt.type == TYPE_FIXED: + raise AttributeError("Option can't be set by software: "+key) + if isinstance(value, int) and opt.type == TYPE_FIXED: # avoid annoying errors of backend if int is given instead float: value = float(value) self.last_opt = dev.set_option(opt.index, value) @@ -187,18 +186,18 @@ class SaneDev: dev=self.__dict__['dev'] optdict=self.__dict__['opt'] if key=='optlist': - return self.opt.keys() + return list(self.opt.keys()) if key=='area': return (self.tl_x, self.tl_y),(self.br_x, self.br_y) - if not optdict.has_key(key): - raise AttributeError, 'No such attribute: '+key + if key not in optdict: + raise AttributeError('No such attribute: '+key) opt=optdict[key] if opt.type==TYPE_BUTTON: - raise AttributeError, "Buttons don't have values: "+key + raise AttributeError("Buttons don't have values: "+key) if opt.type==TYPE_GROUP: - raise AttributeError, "Groups don't have values: "+key + raise AttributeError("Groups don't have values: "+key) if not _sane.OPTION_IS_ACTIVE(opt.cap): - raise AttributeError, 'Inactive option: '+key + raise AttributeError('Inactive option: '+key) value = dev.get_option(opt.index) return value diff --git a/Scripts/enhancer.py b/Scripts/enhancer.py index 957b51c8d..546172f81 100644 --- a/Scripts/enhancer.py +++ b/Scripts/enhancer.py @@ -6,7 +6,11 @@ # drag the slider to modify the image. # -from Tkinter import * +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk, ImageEnhance import sys diff --git a/Scripts/explode.py b/Scripts/explode.py index a336f0699..950ef7b50 100644 --- a/Scripts/explode.py +++ b/Scripts/explode.py @@ -5,8 +5,10 @@ # split an animation into a number of frame files # +from __future__ import print_function + from PIL import Image -import os, string, sys +import os, sys class Interval: @@ -18,23 +20,23 @@ class Interval: self.hilo = [] - for s in string.split(interval, ","): - if not string.strip(s): + for s in interval.split(","): + if not s.strip(): continue try: - v = string.atoi(s) + v = int(s) if v < 0: lo, hi = 0, -v else: lo = hi = v except ValueError: - i = string.find(s, "-") - lo, hi = string.atoi(s[:i]), string.atoi(s[i+1:]) + i = s.find("-") + lo, hi = int(s[:i]), int(s[i+1:]) self.hilo.append((hi, lo)) if not self.hilo: - self.hilo = [(sys.maxint, 0)] + self.hilo = [(sys.maxsize, 0)] def __getitem__(self, index): @@ -53,23 +55,23 @@ if sys.argv[1:2] == ["-h"]: del sys.argv[1] if not sys.argv[2:]: - print - print "Syntax: python explode.py infile template [range]" - print - print "The template argument is used to construct the names of the" - print "individual frame files. The frames are numbered file001.ext," - print "file002.ext, etc. You can insert %d to control the placement" - print "and syntax of the frame number." - print - print "The optional range argument specifies which frames to extract." - print "You can give one or more ranges like 1-10, 5, -15 etc. If" - print "omitted, all frames are extracted." + print() + print("Syntax: python explode.py infile template [range]") + print() + print("The template argument is used to construct the names of the") + print("individual frame files. The frames are numbered file001.ext,") + print("file002.ext, etc. You can insert %d to control the placement") + print("and syntax of the frame number.") + print() + print("The optional range argument specifies which frames to extract.") + print("You can give one or more ranges like 1-10, 5, -15 etc. If") + print("omitted, all frames are extracted.") sys.exit(1) infile = sys.argv[1] outfile = sys.argv[2] -frames = Interval(string.join(sys.argv[3:], ",")) +frames = Interval(",".join(sys.argv[3:])) try: # check if outfile contains a placeholder @@ -87,11 +89,11 @@ if html: html = open(file+".html", "w") html.write("\n\n") -while 1: +while True: if frames[ix]: im.save(outfile % ix) - print outfile % ix + print(outfile % ix) if html: html.write("
\n" % outfile % ix) diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index 95524eacc..3180c13b4 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -39,8 +39,9 @@ # write data directly to a socket. Or something... # +from __future__ import print_function + from PIL import Image, ImageChops -import string from PIL.GifImagePlugin import getheader, getdata @@ -128,8 +129,8 @@ if __name__ == "__main__": import sys if len(sys.argv) < 3: - print "GIFMAKER -- create GIF animations" - print "Usage: gifmaker infile outfile" + print("GIFMAKER -- create GIF animations") + print("Usage: gifmaker infile outfile") sys.exit(1) compress(sys.argv[1], sys.argv[2]) diff --git a/Scripts/painter.py b/Scripts/painter.py index efe307386..d6821e476 100644 --- a/Scripts/painter.py +++ b/Scripts/painter.py @@ -8,7 +8,11 @@ # the image into a set of tiles. # -from Tkinter import * +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk import sys diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py index 1c688f7c9..588c0dfab 100644 --- a/Scripts/pilconvert.py +++ b/Scripts/pilconvert.py @@ -13,27 +13,29 @@ # 0.5 98-12-30 fl Fixed -f option (from Anthony Baxter) # +from __future__ import print_function + import site import getopt, string, sys from PIL import Image def usage(): - print "PIL Convert 0.5/1998-12-30 -- convert image files" - print "Usage: pilconvert [option] infile outfile" - print - print "Options:" - print - print " -c convert to format (default is given by extension)" - print - print " -g convert to greyscale" - print " -p convert to palette image (using standard palette)" - print " -r convert to rgb" - print - print " -o optimize output (trade speed for size)" - print " -q set compression quality (0-100, JPEG only)" - print - print " -f list supported file formats" + print("PIL Convert 0.5/1998-12-30 -- convert image files") + print("Usage: pilconvert [option] infile outfile") + print() + print("Options:") + print() + print(" -c convert to format (default is given by extension)") + print() + print(" -g convert to greyscale") + print(" -p convert to palette image (using standard palette)") + print(" -r convert to rgb") + print() + print(" -o optimize output (trade speed for size)") + print(" -q set compression quality (0-100, JPEG only)") + print() + print(" -f list supported file formats") sys.exit(1) if len(sys.argv) == 1: @@ -41,8 +43,8 @@ if len(sys.argv) == 1: try: opt, argv = getopt.getopt(sys.argv[1:], "c:dfgopq:r") -except getopt.error, v: - print v +except getopt.error as v: + print(v) sys.exit(1) format = None @@ -54,14 +56,13 @@ for o, a in opt: if o == "-f": Image.init() - id = Image.ID[:] - id.sort() - print "Supported formats (* indicates output format):" + id = sorted(Image.ID) + print("Supported formats (* indicates output format):") for i in id: - if Image.SAVE.has_key(i): - print i+"*", + if i in Image.SAVE: + print(i+"*", end=' ') else: - print i, + print(i, end=' ') sys.exit(1) elif o == "-c": @@ -88,9 +89,9 @@ try: im.draft(convert, im.size) im = im.convert(convert) if format: - apply(im.save, (argv[1], format), options) + im.save(argv[1], format, **options) else: - apply(im.save, (argv[1],), options) + im.save(argv[1], **options) except: - print "cannot convert image", - print "(%s:%s)" % (sys.exc_type, sys.exc_value) + print("cannot convert image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py index 5dd575a3f..755b2d05d 100644 --- a/Scripts/pildriver.py +++ b/Scripts/pildriver.py @@ -48,8 +48,9 @@ of its upper-left-hand corner and displays the cropped portion. # 3. Add support for composing and decomposing multiple-image files. # +from __future__ import print_function + from PIL import Image -import string class PILDriver: @@ -206,7 +207,7 @@ class PILDriver: Process the top image with the given filter. """ import ImageFilter - filter = eval("ImageFilter." + string.upper(self.do_pop())) + filter = eval("ImageFilter." + self.do_pop().upper()) image = self.do_pop() self.push(image.filter(filter)) @@ -314,7 +315,7 @@ class PILDriver: Transpose the top image. """ - transpose = string.upper(self.do_pop()) + transpose = self.do_pop().upper() image = self.do_pop() self.push(image.transpose(transpose)) @@ -482,9 +483,9 @@ class PILDriver: self.push(list[0]) list = list[1:] if self.verbose: - print "Stack: " + `self.stack` + print("Stack: " + repr(self.stack)) top = self.top() - if type(top) != type(""): + if not isinstance(top, str): continue; funcname = "do_" + top if not hasattr(self, funcname): @@ -508,15 +509,15 @@ if __name__ == '__main__': if len(sys.argv[1:]) > 0: driver.execute(sys.argv[1:]) else: - print "PILDriver says hello." - while 1: + print("PILDriver says hello.") + while True: try: line = raw_input('pildriver> '); except EOFError: - print "\nPILDriver says goodbye." + print("\nPILDriver says goodbye.") break - driver.execute(string.split(line)) - print driver.stack + driver.execute(line.split()) + print(driver.stack) # The following sets edit modes for GNU EMACS # Local Variables: diff --git a/Scripts/pilfile.py b/Scripts/pilfile.py index 695725796..33c7dc7e7 100644 --- a/Scripts/pilfile.py +++ b/Scripts/pilfile.py @@ -17,25 +17,27 @@ # 0.4 2003-09-30 fl Expand wildcards on Windows; robustness tweaks # +from __future__ import print_function + import site import getopt, glob, sys from PIL import Image if len(sys.argv) == 1: - print "PIL File 0.4/2003-09-30 -- identify image files" - print "Usage: pilfile [option] files..." - print "Options:" - print " -f list supported file formats" - print " -i show associated info and tile data" - print " -v verify file headers" - print " -q quiet, don't warn for unidentified/missing/broken files" + print("PIL File 0.4/2003-09-30 -- identify image files") + print("Usage: pilfile [option] files...") + print("Options:") + print(" -f list supported file formats") + print(" -i show associated info and tile data") + print(" -v verify file headers") + print(" -q quiet, don't warn for unidentified/missing/broken files") sys.exit(1) try: opt, args = getopt.getopt(sys.argv[1:], "fqivD") -except getopt.error, v: - print v +except getopt.error as v: + print(v) sys.exit(1) verbose = quiet = verify = 0 @@ -43,11 +45,10 @@ verbose = quiet = verify = 0 for o, a in opt: if o == "-f": Image.init() - id = Image.ID[:] - id.sort() - print "Supported formats:" + id = sorted(Image.ID) + print("Supported formats:") for i in id: - print i, + print(i, end=' ') sys.exit(1) elif o == "-i": verbose = 1 @@ -73,22 +74,22 @@ def globfix(files): for file in globfix(args): try: im = Image.open(file) - print "%s:" % file, im.format, "%dx%d" % im.size, im.mode, + print("%s:" % file, im.format, "%dx%d" % im.size, im.mode, end=' ') if verbose: - print im.info, im.tile, - print + print(im.info, im.tile, end=' ') + print() if verify: try: im.verify() except: if not quiet: - print "failed to verify image", - print "(%s:%s)" % (sys.exc_type, sys.exc_value) - except IOError, v: + print("failed to verify image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) + except IOError as v: if not quiet: - print file, "failed:", v + print(file, "failed:", v) except: import traceback if not quiet: - print file, "failed:", "unexpected error" + print(file, "failed:", "unexpected error") traceback.print_exc(file=sys.stdout) diff --git a/Scripts/pilfont.py b/Scripts/pilfont.py index df08d4c08..21cfa864d 100644 --- a/Scripts/pilfont.py +++ b/Scripts/pilfont.py @@ -9,6 +9,8 @@ # 2002-03-10 fl use "from PIL import" # +from __future__ import print_function + VERSION = "0.4" import site @@ -19,12 +21,12 @@ from PIL import BdfFontFile from PIL import PcfFontFile if len(sys.argv) <= 1: - print "PILFONT", VERSION, "-- PIL font compiler." - print - print "Usage: pilfont fontfiles..." - print - print "Convert given font files to the PIL raster font format." - print "This version of pilfont supports X BDF and PCF fonts." + print("PILFONT", VERSION, "-- PIL font compiler.") + print() + print("Usage: pilfont fontfiles...") + print() + print("Convert given font files to the PIL raster font format.") + print("This version of pilfont supports X BDF and PCF fonts.") sys.exit(1) files = [] @@ -33,7 +35,7 @@ for f in sys.argv[1:]: for f in files: - print f + "...", + print(f + "...", end=' ') try: @@ -48,7 +50,7 @@ for f in files: p.save(f) except (SyntaxError, IOError): - print "failed" + print("failed") else: - print "OK" + print("OK") diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py index a98b39f7d..9c30e6343 100644 --- a/Scripts/pilprint.py +++ b/Scripts/pilprint.py @@ -11,6 +11,8 @@ # 0.3 2003-05-06 fl Fixed a typo or two. # +from __future__ import print_function + VERSION = "pilprint 0.3/2003-05-05" from PIL import Image @@ -29,18 +31,18 @@ def description(file, image): import getopt, os, sys if len(sys.argv) == 1: - print "PIL Print 0.2a1/96-10-04 -- print image files" - print "Usage: pilprint files..." - print "Options:" - print " -c colour printer (default is monochrome)" - print " -p print via lpr (default is stdout)" - print " -P same as -p but use given printer" + print("PIL Print 0.2a1/96-10-04 -- print image files") + print("Usage: pilprint files...") + print("Options:") + print(" -c colour printer (default is monochrome)") + print(" -p print via lpr (default is stdout)") + print(" -P same as -p but use given printer") sys.exit(1) try: opt, argv = getopt.getopt(sys.argv[1:], "cdpP:") -except getopt.error, v: - print v +except getopt.error as v: + print(v) sys.exit(1) printer = None # print to stdout @@ -50,7 +52,7 @@ for o, a in opt: if o == "-d": # debug: show available drivers Image.init() - print Image.ID + print(Image.ID) sys.exit(1) elif o == "-c": # colour printer @@ -89,5 +91,5 @@ for file in argv: ps.end_document() except: - print "cannot print image", - print "(%s:%s)" % (sys.exc_type, sys.exc_value) + print("cannot print image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/Scripts/player.py b/Scripts/player.py index 9ca4500bf..b1435ba4c 100644 --- a/Scripts/player.py +++ b/Scripts/player.py @@ -3,7 +3,13 @@ # $Id$ # -from Tkinter import * +from __future__ import print_function + +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk import sys @@ -36,7 +42,7 @@ class AppletDisplay: class UI(Label): def __init__(self, master, im): - if type(im) == type([]): + if isinstance(im, list): # list of images self.im = im[1:] im = self.im[0] @@ -65,7 +71,7 @@ class UI(Label): def next(self): - if type(self.im) == type([]): + if isinstance(self.im, list): try: im = self.im[0] @@ -98,7 +104,7 @@ class UI(Label): if __name__ == "__main__": if not sys.argv[1:]: - print "Syntax: python player.py imagefile(s)" + print("Syntax: python player.py imagefile(s)") sys.exit(1) filename = sys.argv[1] @@ -108,7 +114,7 @@ if __name__ == "__main__": if len(sys.argv) > 2: # list of images - print "loading..." + print("loading...") im = [] for filename in sys.argv[1:]: im.append(Image.open(filename)) diff --git a/Scripts/thresholder.py b/Scripts/thresholder.py index eb5109872..9a8610892 100644 --- a/Scripts/thresholder.py +++ b/Scripts/thresholder.py @@ -6,7 +6,11 @@ # as a dynamically updated overlay # -from Tkinter import * +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk import sys diff --git a/Scripts/viewer.py b/Scripts/viewer.py index 6e4dc8b67..8c26e9c5a 100644 --- a/Scripts/viewer.py +++ b/Scripts/viewer.py @@ -3,7 +3,13 @@ # $Id$ # -from Tkinter import * +from __future__ import print_function + +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk # @@ -31,7 +37,7 @@ if __name__ == "__main__": import sys if not sys.argv[1:]: - print "Syntax: python viewer.py imagefile" + print("Syntax: python viewer.py imagefile") sys.exit(1) filename = sys.argv[1] diff --git a/Tests/README.txt b/Tests/README.txt new file mode 100644 index 000000000..7622836b8 --- /dev/null +++ b/Tests/README.txt @@ -0,0 +1,4 @@ +Minimalistic PIL test framework. + +Test scripts are named "test_xxx" and are supposed to output "ok". +That's it. diff --git a/Tests/bench_get.py b/Tests/bench_get.py new file mode 100644 index 000000000..eca491600 --- /dev/null +++ b/Tests/bench_get.py @@ -0,0 +1,20 @@ +import sys +sys.path.insert(0, ".") + +import tester +import timeit + +def bench(mode): + im = tester.lena(mode) + get = im.im.getpixel + xy = 50, 50 # position shouldn't really matter + t0 = timeit.default_timer() + for i in range(1000000): + get(xy) + print(mode, timeit.default_timer() - t0, "us") + +bench("L") +bench("I") +bench("I;16") +bench("F") +bench("RGB") diff --git a/Tests/cms_test.py b/Tests/cms_test.py new file mode 100644 index 000000000..ba1c96527 --- /dev/null +++ b/Tests/cms_test.py @@ -0,0 +1,222 @@ +# PyCMSTests.py +# Examples of how to use pyCMS, as well as tests to verify it works properly +# By Kevin Cazabon (kevin@cazabon.com) + +# Imports +import os +from PIL import Image +from PIL import ImageCms + +# import PyCMSError separately so we can catch it +PyCMSError = ImageCms.PyCMSError + +####################################################################### +# Configuration: +####################################################################### +# set this to the image you want to test with +IMAGE = "c:\\temp\\test.tif" + +# set this to where you want to save the output images +OUTPUTDIR = "c:\\temp\\" + +# set these to two different ICC profiles, one for input, one for output +# set the corresponding mode to the proper PIL mode for that profile +INPUT_PROFILE = "c:\\temp\\profiles\\sRGB.icm" +INMODE = "RGB" + +OUTPUT_PROFILE = "c:\\temp\\profiles\\genericRGB.icm" +OUTMODE = "RGB" + +PROOF_PROFILE = "c:\\temp\\profiles\\monitor.icm" + +# set to True to show() images, False to save them into OUTPUT_DIRECTORY +SHOW = False + +# Tests you can enable/disable +TEST_error_catching = True +TEST_profileToProfile = True +TEST_profileToProfile_inPlace = True +TEST_buildTransform = True +TEST_buildTransformFromOpenProfiles = True +TEST_buildProofTransform = True +TEST_getProfileInfo = True +TEST_misc = False + +####################################################################### +# helper functions +####################################################################### +def outputImage(im, funcName = None): + # save or display the image, depending on value of SHOW_IMAGES + if SHOW == True: + im.show() + else: + im.save(os.path.join(OUTPUTDIR, "%s.tif" %funcName)) + + +####################################################################### +# The tests themselves +####################################################################### + +if TEST_error_catching == True: + im = Image.open(IMAGE) + try: + #neither of these proifles exists (unless you make them), so we should + # get an error + imOut = ImageCms.profileToProfile(im, "missingProfile.icm", "cmyk.icm") + + except PyCMSError as reason: + print("We caught a PyCMSError: %s\n\n" %reason) + + print("error catching test completed successfully (if you see the message \ + above that we caught the error).") + +if TEST_profileToProfile == True: + # open the image file using the standard PIL function Image.open() + im = Image.open(IMAGE) + + # send the image, input/output profiles, and rendering intent to + # ImageCms.profileToProfile() + imOut = ImageCms.profileToProfile(im, INPUT_PROFILE, OUTPUT_PROFILE, \ + outputMode = OUTMODE) + + # now that the image is converted, save or display it + outputImage(imOut, "profileToProfile") + + print("profileToProfile test completed successfully.") + +if TEST_profileToProfile_inPlace == True: + # we'll do the same test as profileToProfile, but modify im in place + # instead of getting a new image returned to us + im = Image.open(IMAGE) + + # send the image to ImageCms.profileToProfile(), specifying inPlace = True + result = ImageCms.profileToProfile(im, INPUT_PROFILE, OUTPUT_PROFILE, \ + outputMode = OUTMODE, inPlace = True) + + # now that the image is converted, save or display it + if result == None: + # this is the normal result when modifying in-place + outputImage(im, "profileToProfile_inPlace") + else: + # something failed... + print("profileToProfile in-place failed: %s" %result) + + print("profileToProfile in-place test completed successfully.") + +if TEST_buildTransform == True: + # make a transform using the input and output profile path strings + transform = ImageCms.buildTransform(INPUT_PROFILE, OUTPUT_PROFILE, INMODE, \ + OUTMODE) + + # now, use the trnsform to convert a couple images + im = Image.open(IMAGE) + + # transform im normally + im2 = ImageCms.applyTransform(im, transform) + outputImage(im2, "buildTransform") + + # then transform it again using the same transform, this time in-place. + result = ImageCms.applyTransform(im, transform, inPlace = True) + outputImage(im, "buildTransform_inPlace") + + print("buildTransform test completed successfully.") + + # and, to clean up a bit, delete the transform + # this should call the C destructor for the transform structure. + # Python should also do this automatically when it goes out of scope. + del(transform) + +if TEST_buildTransformFromOpenProfiles == True: + # we'll actually test a couple profile open/creation functions here too + + # first, get a handle to an input profile, in this case we'll create + # an sRGB profile on the fly: + inputProfile = ImageCms.createProfile("sRGB") + + # then, get a handle to the output profile + outputProfile = ImageCms.getOpenProfile(OUTPUT_PROFILE) + + # make a transform from these + transform = ImageCms.buildTransformFromOpenProfiles(inputProfile, \ + outputProfile, INMODE, OUTMODE) + + # now, use the trnsform to convert a couple images + im = Image.open(IMAGE) + + # transform im normally + im2 = ImageCms.applyTransform(im, transform) + outputImage(im2, "buildTransformFromOpenProfiles") + + # then do it again using the same transform, this time in-place. + result = ImageCms.applyTransform(im, transform, inPlace = True) + outputImage(im, "buildTransformFromOpenProfiles_inPlace") + + print("buildTransformFromOpenProfiles test completed successfully.") + + # and, to clean up a bit, delete the transform + # this should call the C destructor for the each item. + # Python should also do this automatically when it goes out of scope. + del(inputProfile) + del(outputProfile) + del(transform) + +if TEST_buildProofTransform == True: + # make a transform using the input and output and proof profile path + # strings + # images converted with this transform will simulate the appearance + # of the output device while actually being displayed/proofed on the + # proof device. This usually means a monitor, but can also mean + # other proof-printers like dye-sub, etc. + transform = ImageCms.buildProofTransform(INPUT_PROFILE, OUTPUT_PROFILE, \ + PROOF_PROFILE, INMODE, OUTMODE) + + # now, use the trnsform to convert a couple images + im = Image.open(IMAGE) + + # transform im normally + im2 = ImageCms.applyTransform(im, transform) + outputImage(im2, "buildProofTransform") + + # then transform it again using the same transform, this time in-place. + result = ImageCms.applyTransform(im, transform, inPlace = True) + outputImage(im, "buildProofTransform_inPlace") + + print("buildProofTransform test completed successfully.") + + # and, to clean up a bit, delete the transform + # this should call the C destructor for the transform structure. + # Python should also do this automatically when it goes out of scope. + del(transform) + +if TEST_getProfileInfo == True: + # get a profile handle + profile = ImageCms.getOpenProfile(INPUT_PROFILE) + + # lets print some info about our input profile: + print("Profile name (retrieved from profile string path name): %s" %ImageCms.getProfileName(INPUT_PROFILE)) + + # or, you could do the same thing using a profile handle as the arg + print("Profile name (retrieved from profile handle): %s" %ImageCms.getProfileName(profile)) + + # now lets get the embedded "info" tag contents + # once again, you can use a path to a profile, or a profile handle + print("Profile info (retrieved from profile handle): %s" %ImageCms.getProfileInfo(profile)) + + # and what's the default intent of this profile? + print("The default intent is (this will be an integer): %d" %(ImageCms.getDefaultIntent(profile))) + + # Hmmmm... but does this profile support INTENT_ABSOLUTE_COLORIMETRIC? + print("Does it support INTENT_ABSOLUTE_COLORIMETRIC?: (1 is yes, -1 is no): %s" \ + %ImageCms.isIntentSupported(profile, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, \ + ImageCms.DIRECTION_INPUT)) + + print("getProfileInfo test completed successfully.") + +if TEST_misc == True: + # test the versions, about, and copyright functions + print("Versions: %s" %str(ImageCms.versions())) + print("About:\n\n%s" %ImageCms.about()) + print("Copyright:\n\n%s" %ImageCms.copyright()) + + print("misc test completed successfully.") + diff --git a/Tests/crash_ttf_memory_error.py b/Tests/crash_ttf_memory_error.py new file mode 100644 index 000000000..881fc03a6 --- /dev/null +++ b/Tests/crash_ttf_memory_error.py @@ -0,0 +1,14 @@ +from PIL import Image, ImageFont, ImageDraw + +font = "../pil-archive/memory-error-2.ttf" + +s = "Test Text" +f = ImageFont.truetype(font, 64, index=0, encoding="unicode") +w, h = f.getsize(s) +i = Image.new("RGB", (500, h), "white") +d = ImageDraw.Draw(i) + +# this line causes a MemoryError +d.text((0,0), s, font=f, fill=0) + +i.show() diff --git a/Tests/fonts/helvO18.pcf b/Tests/fonts/helvO18.pcf new file mode 100644 index 000000000..f5e68ae9c Binary files /dev/null and b/Tests/fonts/helvO18.pcf differ diff --git a/Tests/icc/CMY.icm b/Tests/icc/CMY.icm new file mode 100644 index 000000000..acc71ddd9 Binary files /dev/null and b/Tests/icc/CMY.icm differ diff --git a/Tests/icc/YCC709.icm b/Tests/icc/YCC709.icm new file mode 100644 index 000000000..d9f5a5a5d Binary files /dev/null and b/Tests/icc/YCC709.icm differ diff --git a/Tests/icc/sRGB.icm b/Tests/icc/sRGB.icm new file mode 100644 index 000000000..7f9d18d09 Binary files /dev/null and b/Tests/icc/sRGB.icm differ diff --git a/Tests/images/broken.png b/Tests/images/broken.png new file mode 100644 index 000000000..7def38564 --- /dev/null +++ b/Tests/images/broken.png @@ -0,0 +1,5 @@ +‰PNG + +/Å}Pô¨áÌ’ï>ƒ’0jÈ‚&ë✉\Þ@äÖô­ T´LU +¥í÷âxÃÅ/Ð +Z`ײwëÙ;†9 âcˆ€ \ No newline at end of file diff --git a/Tests/images/caption_6_33_22.png b/Tests/images/caption_6_33_22.png new file mode 100644 index 000000000..2aa0a50bf Binary files /dev/null and b/Tests/images/caption_6_33_22.png differ diff --git a/Tests/images/pil123p.png b/Tests/images/pil123p.png new file mode 100644 index 000000000..03960d493 Binary files /dev/null and b/Tests/images/pil123p.png differ diff --git a/Tests/images/pil123rgba.png b/Tests/images/pil123rgba.png new file mode 100644 index 000000000..c3d7b7d1b Binary files /dev/null and b/Tests/images/pil123rgba.png differ diff --git a/Tests/images/pil136.tiff b/Tests/images/pil136.tiff new file mode 100644 index 000000000..07d339e7c Binary files /dev/null and b/Tests/images/pil136.tiff differ diff --git a/Tests/images/pil168.tif b/Tests/images/pil168.tif new file mode 100644 index 000000000..621f03a2f Binary files /dev/null and b/Tests/images/pil168.tif differ diff --git a/Tests/images/pil184.pcx b/Tests/images/pil184.pcx new file mode 100644 index 000000000..bbd68be5c Binary files /dev/null and b/Tests/images/pil184.pcx differ diff --git a/Tests/images/pil_sample_cmyk.jpg b/Tests/images/pil_sample_cmyk.jpg new file mode 100644 index 000000000..0c11e70f2 Binary files /dev/null and b/Tests/images/pil_sample_cmyk.jpg differ diff --git a/Tests/images/pil_sample_rgb.jpg b/Tests/images/pil_sample_rgb.jpg new file mode 100644 index 000000000..956a86bae Binary files /dev/null and b/Tests/images/pil_sample_rgb.jpg differ diff --git a/Tests/images/pngtest_bad.png.base64 b/Tests/images/pngtest_bad.png.base64 new file mode 100644 index 000000000..44e85d644 --- /dev/null +++ b/Tests/images/pngtest_bad.png.base64 @@ -0,0 +1,151 @@ +iVBORw0KGgoAAAANSUhEUgAAAFsAAABFCAMAAAFlM1rWAAACAHRSTlMAALGPC/xhBQAAAARzQklU +BQUFBU2lLfYAAAAGYktHRADgAOAAgJXNLyAAAAAJb0ZGcwAAAAAAAAAAAa0thlgAAAAscENBTGJv +Z3VzIHVuaXRzAAAAAAAAAP//AAJmb28vYmFyADEuMGUwADY1LjUzNWUzV0B7HAAAAAlwSFlzAAAL +EgAACxIB0t1+/AAAAAd0SU1FB8wGBxE6CI7/JnoAAAAJdEVYdFRpdGxlAFBOR9wBeTUAAB+3SURB +VHic7ZxPaGTJnec/OZOC3wMJIsCCF4YyxEAPPMEaUgdD5oIPueABFcwhBWuo2pvmMFB9U82p+7AD +5Zvq1nUYsOamOhiUQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB +QUFBMyPj/SLi9+f7+/4e/CFkWNjVz4+GK4C/ePXN9RsHPbsC+ORouMqVMJ46ALoAD4bFeeXr0bBn +GF+6pyLkAjzoFyujhOezsgOQK+n/BcDzSXmYK01hDErk+MnDoXehHgPEmH7toGdXVYizzZRCjJSu +JsugdJ6j4f7IVTV1jBgtKJHrKQH42DB1FQA9q/jwdNIBqD6Nx6N9ewLx9s0+GharB8NiBfDRqLdK +X8xHb1TdWjvfrfzrR6PVsMhXAH95840no/75pPzyFwBnxwerefnFzucvXwyWy4x8Rz7r3JzzzHmK +3PD4+aQzPTlanU8umZRuNndhAK0aq/pahb52/N9PT1azyRRjBON13/T0aljk1zoHMFq48jU/e3o6 +bhpGoV2gpoGeNWmT5TqjXTD2jOZ0Uh4O9wwXc9fpF4ZIZO7C9R2P2h14fnzwRp3/BUBsfw7g8OlF +59uq+Z1FhLyXq9FBz/r1/3Xf9oW75JOj4SoCj9tNenTQWznnUZIhIsQbm/XWKT0/Plh9cjRcnTy4 +3osnR/3V2Q09nU2vsEoB6VD2jUYkI8SGKkTGM9e5c/Cp80yvPGXl+eRouOoX+cr7yHTu+PSjUfqB +SIUINlcraxWZCJO5MwBKwaOD3upf28/eGty06+/riAAZMHeBS19zNplzdjxazV3Vm85L+lZTVQEl +wkG/8Eo0RPA+3K0WgIE1GC1cXDqs0VgjGJWRZcLR07E5Pz7wxgjPZ67TNBCJFEbQAoXNMVooq/D6 +4IXRTFyygNLexemk7Az3ctJAVPefXnSEDIDns7Jz+PSi87PxvNOzBqMzLi7dYG2RbsknR8PVqG9X +j4a9Vc+qE4CDnj0/edBfvW3zf6MoIf/WX/6Tlgd9ewK3bcdru+V9RYnkH436qyyT46NhsVr7n/ce +fFjko58fHayOWtcDcPJw6EufDG/TQBXecPxflU8eDFefHA03hsj5wMw5zialATh5MFzNvUcQ6jpS +hcjZtHx98J5Voyej/upfPxqtjg965wDj+dWM5noXKS2DZJyoHvSLlWSC84Eq1ETA1/X4Yn6HbTka +FudXPnA6KRn27OjRQbGaltXIGOHJUX+tBut94EG/WFWhZt8qSueZlFXnYu46ZRUO1+q7Nbhv0r9Z +Bq4KiAijfuFLX6PIOBr2TuYuHIbYMK88TUw2yIXYKXJ1vo4+zo8PVlNXTW8NbrJ0pOs64nyNble9 +iZGLS8fxaHCc/oZhYdmzmhgjwyJfWaM3sYQAA5sPbg2ur3cQWgmXrqIKEWs0SmBaOq4+ebTSKqNn +DXUTaYiIZAjJFgkwdzXW6NuDu7qZRSKBhp7RaAQlyeyGCB8+uzDz0m3iqBAaQt0wKJIlXP9AE0GJ +vDJ4Fab7NuejgwFPJyUu1KzVYLRwdNDzH59NB/f3LUZgUlad85mjMBrfRKwRdAZIxCrdvzX4+dSd +CPDh8wm5CNZotBaqpqmaJnl8F8LM1/XGS85c1YkRHg4KQBARBtbw4fNkcm+GlZWQYZXeLEzTUJ2M +5z2jBV/HGcDj57OO8/VmfR4+u+hMSz/TkrFvdXX/Rqhxy/tHwGppZyY8n83NQc+eIzCZu82OKKsw +vvm9pxfzAXfIreOvsrQoSoRnk3kH4OGgGOn2VK4/dzopD+8a7K1yfnywWsfBv1dRIv85PVGRq9ey +iT98gPoecn58sCpDDQ1YrRGB0tX0CsP9n41vzfW9Y9xvKz2bjz4e7T+7mLuz00n5+OZ7n340Wvkq +8uziklwlW+t0oGlg3ypijK+N9wfVeK6kv2/NdFhYjBZOJ2U1KVM4DCm3He5bBJhcOiINSpLtiURi +TDlwYdTmRK/lW2v8oGf9w4HNfd1gjebJeHo4d7ftjdHJppeVR2nLetJKZHQ4sOejfkGIkdPJJUKG +Ek2IacIAIulyPqCEPMRr87OZeLuU53WIVHWDCNQxUrrAvs3pF4a58/gYGU/d4cXcmYu542hYTJXW +/Y9Hg/NJ6WfPbhlHGdShwRrFvk2KftAvVlmW/HoIkTpGmgBIQxUBGqauGsTI7G2Ke22rKCF/POr7 +BgjroCym7OygZ6ljZHzpsJmAgFEKHyKjnqUKgToKH55eu4p+ka8y4KOjQ56dXRBig5KMQZHTs4YY +I/ffkMX2bT6637fPhCwvrDAv69nH49ltz7yWEKkE2Df61qTTy0ioGw72ci59TYzgq4BRwtOLOSKC +VnD+0dEqV8l2hzq2eE1NIHl6SO7OVREXIsMiRVKjnl2tXx/07Eqr7PyyrPLCKkKIrCd958QBpI26 +uHGar3yNqxskg9BA5QOfXjouXY3SGT40nE5KrlyN0fDs8UN//uR46kM8qyOMJyWj/QKb65TERdAK +ilwBDdLejEoLubkgKadOQNXxWye+Ft9Eyjri6kgGqCzDKM2lq+gXlkFhsLkm+GaT6s9dxeDxaSfW +HoPvnx2PzlNsFrFKiDESiVy6CudrqhDJld7EAyKC1umS9u+qbihyRWHU2zXufD2DZIYKozgoco5H ++8QGzqYlrmoIsUZI2ERhNUZ0ih4jWCWjh08vOleuwofAw8Eevo74EJk6f+irhrmvcSHl18M9g9Zg +TdLxesI6awP5EAgxYtR1DH3nxCeX7iTGSCZJQ5Oy4uPnU85mV0gmWJVMl9aCRvA+0msdRWEV/cKc +A/zd6aQTY6xEYLifY3OhCnE8c1XHVaFTunrWxIhRQmEMIGRZsmZ1HRERrFL0rcGH207ojQ4ooUAN +ZxNHkWfEmDSRZSkN1BnUDYQYZ3VsTu4X9tzHgCCtbS43Yxe5Gh0N98/byPm13xTInzzoe9WerdiA +jzVWa7J2oz98NnlHB9QkVKTIA1qnLRCb67sWEZ7f8GaPD/bxZSCmFbsVm5ZVGH88npiTh0P/qiOB +hK48fj57Ly/+xsOp9fW5TmBSRDJQIsxKd/j04nrSR8Ni5UOkrhN45UIcvzpejFQJ1pFX3/pW8sa7 +PHuUsIw61jgfQZJDurkFIIHzztVnM1e9Ntk/pNw58ZMH/ZVkCdGclW786aU7qUJ8qwv+s/z/ILmS +/n/0HL5R+kV+fDQsTpSQ50pGTx70X0t+v7Os55vkyajvRZNHoFCaIjfHhRFCwmfyEOPGbP7lW8b5 +zuTkwdBv72zl/zL9nOar3zL//CVfvAz8ldmhjkvcy69mVVhssMjfGaF9FxEhP+jZ458fvY6sPxr2 +piqT/GxyRa4yQkwZjtbCp5ceAfatvoXS/EG3x9mjgxWSQtIQ4fTi8pZntEqOhz3TF5EUTYaIUi1A +2RY4Zlee3p4ZwB2g7x9CPr0s+XRa4gN8OnVMysrceDv/6OHgBOCshQ2Nufac64TYhYh5xYJ8K03b +XPU/ur8/rpom71vDf3sF07j+YZDWhb8KIYx61oNQh0gVarRkm2+FyK2EuHT1re9+K037KrjS17nV +GXMX3loCDTGSCUyd3+zLUc+utE4hbtWASIYSwdeRKtBGmekiwtm0fHpzzNc0rYT8cFA861kzik1D +zxpcFdBKePj0wkSoIlRPL+ad44PeqrAp5xz17cl45m4BOCE05ColDkYrU4WKvs2niHDQs4jApEy1 +tlQFaRBJoXEdm2ruqtFdWfxmWT89Hq1CDNSxwdeRfWuYzB11Aw8HFh9SwjW5ctV4dg3UnB0frJoA +Jof7P7udgSeNpgDfxTieXjpXWHMswIf3e9RN5GxW4ly9iaV9HcfrmsWbZLM97j8ddx4+m3QiYI3m +op1wiJHx3LFnFbGBwV6ePxoWq2GRnwOpJKHAh8gnd7ATBPAxkouYwujjGCMhRpwP+BAJ9TqnDIeT +suq8acJKJH/QL07gju3h6xSOCiBtmhVjZHLp6RWauWta0F+PHlm9UhmUvkbIsEbo2Xw0b8PVi9IN +BtZOrREGPduflQ7Vlq7bOg+l8wN3RxSZK+kPCnP4cFAc1yEd6b9rsZfXDqIWqZTOqJtU3KnrdIx9 +jMzLmsJoQmhwdWwPTMaVqyiswsfIo4PB+XqsGJn5usbXAa01vo5MXao00eaMdcS1mhz1bD4dFvlq +1C9We8ZMQ2ADK2Q38ojXJl2HNJn9PYXWKVNOKxDITYbzNQ3XuUioG7JMeDqeEmPDntFcfvJos03W +vIk6RiQT9q3BqAytkjF8OCj8sLCrQWHOjcr6kBFjRFqndFl6YgO5ekst1NXhDMD7lEo1rcZDbMhu +fE5Jek+yjKaJNKQkFRrqAP3C9gGufDrcWgSbZ6lYJYILdbLHxA3VBLj1OgKujmidEug3Trp09VQA +Y64xieSKM5xvMDqjqiOTMrElhOSitWRMy4orH8gELk4eTQG0JHKAEBESOSZJRh1SRn8TdXr1ijG5 +duffMum1Y3J1pG7aGCCCDw09q3G+QWWwZxRaNM7XiECMDUoLs9JBrLmcl5wcHUyt0eRKU/q6RanS +tV5tm+trT7JR881EOCEDcmOZX7MezodpJm2GHa/pHUZllD6SmwzrdJu5pynoTAihofI1udJMr6pK +MsmNSF+17JnmxvK25xCjEtvj0t2cY8JbaGRjYSKRzR93aTpEqiYma2HaYl8EhtaiNMzLGmtU4rq8 +oiAlGZHI4+cTowVyBff3LUQwAr5qKKuEypY+VfbXUZ3O2GzHprmeY0YyDjc1fWfskQkUOrnnwihG ++xatBecbSu+Ze7/hyZUuUBiTys1yjZt8cnF5CNAQ2S9yXIiUPhgtUtWh4eLS43xNiKnQq5RQN+nb +Wkt7E6kUXdUN+m3WI/1QMmX9wlAoTWgazi9LJqWjiZBLhlKyicSMSojomuDTt/n5zFXjsgpUdcMa +ArNGDSZlZSLxaZ4JPkS8jxitmbvQmrpUNldKJctCIrMB2DZEvTvKi1BYzcWlY155Pp07fGgwKkvB +ekxL2TRgjNqAhkK6Ea0yA/Dh6aSjsixpoQEtWIC5C4/nrjK+TrY7Nql2r3VyxVWIVCGQK8HkagP+ ++xjdGydtc0XpakwmXDpPjImulzQbW62mJYwxtqD5tT11vt54xdL5sTGSCAjWbMLTCNXzWdmZO1+t +gX6r1Gbb5SqN1aSogou5expbTPDOSc9doGeTmRIRijy54MIodJZocOkACqeTsmPyZG2VQN1EfIjT +9VjPJuWhkEzWTcdx/f7cxKapasCajH2rKXKF1RqrFBlwOrk6vFm3fEOJg1npIz1rNhmF1Wnp65Yh +IcCzi7mBZL50liJCqxUh3g6AHp9NB7GBfWvuJLA+fj4zz8azlqYnFEYn6l5ZPf14POu8ihfemW75 +umFgNT5ccxzqBuCag3lx6R6vqQg+xLcCo2UVZpK9HTuNkepVXPpNcqemSx+mqcKqN14xgzZugFnp +x2UVrlOgJsHBdf0aY3gjj09nnZvxw+8ib7Ae0dcx2ce15GbN1IlMymoTqA97KRnYMzpxGJ1/I/T7 +8fnlnSyT38ukS187kwvr/M+28JQIPHsFp7Zaj9apf2E1vo5nb/oxV4XfC2T8JgjB+zaPt0YofcBk +wtOL8iZuQc+qUdMwPnx68f7Umt9B7py00fqQGAnt7tCZcH7pDm9ygADmLoxfLfx/F/LapPtFfvLo +oDiOEZz3s/GlO3mVAfVHJ3fRd/4sf5Y/y7eVXMnoQb/wj4bF+Td99o+m8vLHJjZX/eG+eWqN6qes +LMPmgqtaCKuuZ1WI/aaBwZ5BhAGT8q1j/lnZN6TIVf/jw8HY1zG3Rpg6z+SygtigVIY4Ibacj+G+ +6Q8LRVkFtAi5kvzVctyr8kdRnvuPFiXkT/778JfDPfs/d78n2y9/G3l28e+E8DX5NuzqHeJiySIu +2RbIsi5fvlzwYrHgBy3IqrVQhUX1+VsCxD8qvu37ihLJC6MG+9Y8HBRmJJKSqgTQCtPScTYtB28j +VQyL/OTBcO9YtxDO6WROjA25SoBkaPN4EciyFprMuAGARfZtjtGaK+/HPxvP3xgA/8maEaukP9y3 +07qJ2Dwpak3Mm5aeuo7MSvf4TYpWQv/hoJiaXKElQUnOe4xOiWRKMBJauZamSUBefYNLGYFJWXFQ +ZAwLO/oZ8zfO+T9M2SLkPZs/tEqbgTWD09nlyXtmGWaNTV+6ChvBaI1WQnSRi7kbvIpvrKVn1XmR +m9GwZ1vuaiQ0DaUPxJhQxlz0BtZdA+2RSKyvoeAb98LUeSRLdr98gyn5TpX9aNibDgvTVy0UVsdI +5Ru0Fk4eDM8nczf72Rv6RV4TuVnSFqoQsC0peOr807sUrURGw8KciwiHA4vzkT2j8SFyWSa/tq4q +r5VcNy03PEIq3mQbXOcmkhZipPQ1PWsOf6/KzpX0tYjpWT24v28ftj+dXzlfhch0cuVO7vrBZ5P5 +4HQyz4c9O1ci+XDPkOuMuk4Y5LBn+1qJfzKe9V7lJr4qglgfG7SkQpnOJFX0ohAbbsW8IvSHhR0r +kTwSsUYBgmlhh9g0G/xp3aTl65h2+UYyhGsETdoFcHU99iGchMjs4s0WBHiDspVIbpQMelY/3DNq +dHMJrU71AB8isWlwPnUZ5CrZBRfiqMj1iBZDHU/deDJ3j9bgRITqou0YrkJ9PhrujUxb2woBCqPz +J0dD//h0YuJbFC4ZRktifu/ba1Z3bgSVyQa96dt8alTWX5uEDOH+vsX5hI6XVSqGx7bGfFvBbOgH +AD42M+fCSYxx+irY8i5yJy3X5mrQs/rhYC8fRcBIhqsb1o54feQQkCwVEpsm4WvDwgKJMF35htxk +qaMiVVhn3sdbQMuDvj0f7NmRXRcT20WqYsPZp+Xhm0CZns3PrcpGITYb8yEIRwf7nE4uxxeXzvWs +PqalMEfSYlojjPoFkIDLBhhPS5yvN61yAE2kcnU4qUJ8etfvfxv5xtCvyNVoNCzOdbYuy4Fz9YaH +1NzYCFVINPzhvkUpwVeB0H5g6ip0JjRNhEwwIkxK91iLMOzZkzpEhj1LCBFfR2oa9rRm6l5tbUmS +KxntW/MsvbwO0R4dDolNw+nFDBEhhEhsCTIisG9z8kyzZxVXPjV0PHk+nZU+TEOMJ3yLHbveoMM9 +89DkyghZv2cVcxdmzy6mx2smxjfa7LIKY52ldDXUDb7loqxlTWYQAaNVq1iPVQnW1krwoUZn6XkT +RmVokmfft+ZEKUFpqGMilz0cpl1HDY3AoDB9a4x/cjbp3czOqhDHF3M37he5r0OTZ+v6D4LWEGJq +DkFSjTW0TJHCpqbG5gbvvWri8ZsiF0iOVQRrjXqcQZ4rzf39xHqqQ8S0abyQanFViHx4OnntVL6T +g7y8qsbDfTsySiMh4klNIzd3ddNWb9bSs+DrVHy2JqNnU+dNjHAVktJl3SvTNgxmWeR0Mme4l1rg +fIiYXKFyyU+PR/7ZxexwMne3biDU0alM8nUrcyIxtesVm3ZKQqokZBAhM4kZuGcUVy6gMhkUuXos +mQy0kMuN6vea45ZeN5ui+/m03Nztwb7FKsGFtBHb5GpTC1zLO6XrH9zTP91BipchItkWXwNxEcl2 +hCxbJseTJTMSll+xWEZ2t7f5wGiWnQ7Vi1SX/s2LBSKwwxZmNzmsbhe2ul1eRljGCMsl//a5Z0nE +7O4QI/zAaLqdLX68X/z03r3dnV/NP//lem47XdSLGH8inS5fs+SDe4btrS1eLhaEsGB3O0PLFrK9 +hQj89Q+26dIBunS7sFgs+Zr4k+VyWZjtbJvu1q1773Zvvt5iCXSXa1Zl2q0xwgd2l7iIhHrJ7k6X +f/5V+Q+v6vGdGJOuqs8Kqyms3qzyNZEL6poNUyYRBDR1SN1IWkhcqabBhxpX1bjQcFmmxoo1hdPq +FG6VVToupauZXvrkI3yNaQkH9/u9Y//pJ5snMKyd9vr1mihplN50sicWRMv5aRMhfSO1X393cy5b +mukmvr7xmhufW/+XqwMhtE1RNPh4dxP2Oyk7NunmS1eno5RlaA2JRRI3yYVIoj9VLalXAOebROit +ArFZ87nihuW2Pv5GspbTlcZQkgGReenwIVA6j5GIc47au/zq5x+tijzvhxDPrEnMDC0Zl1cO72vy +9vlDNy9I5mOjrJiYTLlSZO2SrXEQJSnmFpXoK2rN4eHGJdd9dr6O5EpQmSbUkcOBvcV6hXc0Izvb +oo5+/NdHxmTEZYcvvmqYO8/Ll5FFXCLdLRaxIcYlXy2X7EiGkm2+r3cwuxkv669wLwJbHdjJdtDb +Gd1uOta7O8JymQh2i+WSL15Gtlgi21ssIiziksvSz0Jc/np3W4oXiwWhWqCzr9m3944iy0ro/CST +DCWg9TbbWTIFbvNgnkhkyTIuWSwi94wQY6d9+E+XnWyL0r9EidDtdm9cyUx04ZX/b6/2vSWwnXUx +O1vsKiH+NrJkWf2qrH7x3soOi/il1d/7+5chbne3ttjaWvJV/VuWbNHtbrGgoVnClmTsbm/x4w9y +9u9p3Isavu7A1hb+RWRbOuxqoUsXvdOFZZflEhZxWd3b3tl+uVhQh8hWF2JcpgUBfmB27p1NPhuW +VT27/1/MT79aLIhLgCX3dnd+srPd5fMvX9CVLZYtE2dXhP/12ecs6RCXre1tr+3tLltL0Drji7DA +bHdZ0qXzdaSzBVvttVy+rossg60lbGXXn8u2YBuhu9VBbwthEcmyTF/M3a0Y/Z1Z7ns2o9fa7LpO +cOONKWAkw2phuGeBDN/SNlxdM3e+NTXpuK1xh3UImWUp2chVSrlTTHxND01UPLFzV42Pnl2YTAQf +AqrFRySDg94eIVy35PoYqSNP69jgfM3cpat0NeOZS82fIbbZt2DaCKRurqMsnXHD5qe/29sFbpuo +0Jb0b7QgvGaz37l4oLsyiEuK7tYWu3qL3Z0u99Q2dneXv/1h6kHuLrfQO8I93eVXn3n+pfySL0ME +lvhFw29jh+1uAt+3tpKJoLuE5XLx1/d2t3W2RekXyT7uCC/rFJ18YBRatnRZhV/EJYvxrz//x55V +f5NtZffMdpeXdURlW/zog3s4v2BXw0434+zfysP/U4V/WCziP/3oA/M/7u1m28vfgtAlxK+xRnNP +CSEuybIuyy64akFcLtnNuogI2wLShRCXrDe6AF0Rsqy7uegmX7O1tWR3W6jqJf/7c/dPccnivZVt +dnfu/e3g3k863SUvXzS8WER+83LBZ1+85DNf8auy4sUi8pmruCi/wC8WLJdLdrpbdLvwA72DdLss +lkuWLNE725BMCNKV7awLO9kWLxeRra0uizqyLcLu7jbbAotI8cG9nb8vv0xEpl+V1WlcLnd+ZHcH +LxcpTHyxiPzog10WdQejt5l//sWsWsRyCYvPq/C0/DL84yIuf/nDe/p+V9guvwj80O7y1QJ2tyEs +OhQ/EJZ0qZdLFssFHcC9WJAtu8iOsN3tsr3dRXeFnW6XbndJeBHpdpaY7RSq6p0ui6+XlF/Us5eL +uClMvrOytzOM2d796cuXDd/byfi+znAvF3Tp8jJ+xVdxSRUCIcK97WS7v693WLIkBpLid7rsZF30 +1jZxGdnagl29TaeTdsOidWBftIrWGWmBFpGwjPiq+eWXYbFxOp9X4Zdl9dVnP/7h938aFku0CF8s +arpbS7TawVXhy7IKv7x5H3G5/LKswlP3ZfgnlclfAcXuzg5bAnGxRO9usVzCdlfI6LIt23z99ZJm +CZ1OMv5hkSCFF4tI5+sl39sVBsU9ll8v6W7BjnT558m/P/71b17+883ffueymFWqf/JwMF2Xnlxd +Myl9+ziCBAIV+ZrbHonhumsObmMoa9tXk8zf9Mo/Lozm4bA4WffoZO0jjoQUx+pMOJ/eXRAQIX8y +6vuibUjyTYPJMmLTzB4/n30jPi6QnxwNvRahjhHfNOzpjCZCaBrqyIYEnJFAtobEVS9MIiLblh77 +6aUbP5/d/dild8azXQgzhA0yt28NLsTN4zZiTKUkaTm3oqXl4cqmjJSeq5Ue96a1QB2q07bRKUL/ +CBIhuAqbWDeRkYW6idWb8IsYUx/+yYP+VMj660ejKZ3Zd7m3yLpVHkY9ezLYz49TzK+JwL5JT2ew +JiGTB72EMpa+BgHnwuzJeDb6Jgz+vQq+R/3i/GBgRyKCc4F55fE+bnZwao+4rtXRku6z9pkBIcb1 +v9X51I1eVd7PH6WmrHnpSdWclKVaI1zM/dO5q15LFF6Vg549Ge7nxzRQWMPDp2PzTUp4kyghHxb2 +sU4Jkt23ykzLajr39fm3oVm+V6VGMhwxJQvGCGQGIWXO6waBm4omS0ctZVcgUZg7P75Jft2MLeSh +fdyRtAtT14nlGyP4OrwG7NwlF3P3+NL5848PB9MQIoXNB7Py2z1LIUSq8fy6//H578hifa9u0tLX +3rcPOYmtPbsBGaSuPZLSc5PSZZF1b03kdDI3dyk6V9J/OCj8GoNY4y9aC6bd4dUdT8p4k1Qhzj48 +nXTqGCst72ZKvgt5r51d19FZlVhAVgl5lnHVXHeQWKOQ1s5WIZHlqxCZXLrDux4rAknR9/ft1Lbt +BXVMRdhLX28Kq1f+zVTvt8mHpxPzzZ/67uQ9zYhsTIhrHYZpn+uyVrJvqzURwdXhVqfvq5Ir6R8N +i2lG8uoA00s/1hlMrvxZjNHf1W/6pyrv5SBtrvqfHA2nrt3ZpY8onRDBECO+iZg2jX9TmPafWd45 +qelZdXL8N/unLxcRI5KA96+XCfwHdncFlsvZ51/Uvxj/+jf/NS6XX/4B5/0nKd+4s0d9ez6w+cjo +DFc3s0tXTeeuPvchuLdRDf4sr8v/A0hBK/T1IM6VAAAAxnpUWHREZXNjcmlwdGlvbgAAeJxNy0FK +xEAQBdB9TvGXCjNhQPAE4k4d4hDXPd0/SUFS1XRVRnJ7t779G6iFjQX3A1c2Fw9qJmzCKC6meIrt +GUM6cGsps3Ujm4spXvrLCbuLzoiFuMlGxyd/MdiWFJNpIGnB28f7j1l57btv0Uxcv8bzkA4Uo0Mt +UKQxr3HA91qtBUSDbU2ZBbZH3ePUxSKOSVZiSY47qcimD7ZgQRiS/l+ypZl4SELqqnmca7NMd9EZ +Hqz9H24IVyP3c8UAAAAAAElFTkSuQmCC diff --git a/Tests/images/rgb.jpg b/Tests/images/rgb.jpg new file mode 100644 index 000000000..fa9953844 Binary files /dev/null and b/Tests/images/rgb.jpg differ diff --git a/Tests/import_all.py b/Tests/import_all.py new file mode 100644 index 000000000..118bf69a7 --- /dev/null +++ b/Tests/import_all.py @@ -0,0 +1,13 @@ +import sys +sys.path.insert(0, ".") + +import glob, os +import traceback + +for file in glob.glob("PIL/*.py"): + module = os.path.basename(file)[:-3] + try: + exec("from PIL import " + module) + except (ImportError, SyntaxError): + print("===", "failed to import", module) + traceback.print_exc() diff --git a/Tests/make_hash.py b/Tests/make_hash.py new file mode 100644 index 000000000..7e1a1b2e1 --- /dev/null +++ b/Tests/make_hash.py @@ -0,0 +1,57 @@ +# brute-force search for access descriptor hash table + +import random + +modes = [ + "1", + "L", "LA", + "I", "I;16", "I;16L", "I;16B", "I;32L", "I;32B", + "F", + "P", "PA", + "RGB", "RGBA", "RGBa", "RGBX", + "CMYK", + "YCbCr", + ] + +def hash(s, i): + # djb2 hash: multiply by 33 and xor character + for c in s: + i = (((i<<5) + i) ^ ord(c)) & 0xffffffff + return i + +def check(size, i0): + h = [None] * size + for m in modes: + i = hash(m, i0) + i = i % size + if h[i]: + return 0 + h[i] = m + return h + +min_start = 0 + +# 1) find the smallest table size with no collisions +for min_size in range(len(modes), 16384): + if check(min_size, 0): + print(len(modes), "modes fit in", min_size, "slots") + break + +# 2) see if we can do better with a different initial value +for i0 in range(65556): + for size in range(1, min_size): + if check(size, i0): + if size < min_size: + print(len(modes), "modes fit in", size, "slots with start", i0) + min_size = size + min_start = i0 + +print() + +# print check(min_size, min_start) + +print("#define ACCESS_TABLE_SIZE", min_size) +print("#define ACCESS_TABLE_HASH", min_start) + +# for m in modes: +# print m, "=>", hash(m, min_start) % min_size diff --git a/Tests/run.py b/Tests/run.py new file mode 100644 index 000000000..1ce31448c --- /dev/null +++ b/Tests/run.py @@ -0,0 +1,95 @@ +from __future__ import print_function + +# minimal test runner + +import glob, os, sys + +try: + root = os.path.dirname(__file__) +except NameError: + root = os.path.dirname(sys.argv[0]) + +if not os.path.isfile("PIL/Image.py"): + print("***", "please run this script from the PIL development directory as") + print("***", "$ python Tests/run.py") + sys.exit(1) + +print("-"*68) + +python_options = [] +tester_options = [] + +if "--installed" not in sys.argv: + python_options.append("-S") + os.environ["PYTHONPATH"] = "." + +if "--coverage" in sys.argv: + tester_options.append("--coverage") + +if "--log" in sys.argv: + tester_options.append("--log") + +files = glob.glob(os.path.join(root, "test_*.py")) +files.sort() + +success = failure = 0 +include = [x for x in sys.argv[1:] if x[:2] != "--"] +skipped = [] + +python_options = " ".join(python_options) +tester_options = " ".join(tester_options) + +for file in files: + test, ext = os.path.splitext(os.path.basename(file)) + if include and test not in include: + continue + print("running", test, "...") + # 2>&1 works on unix and on modern windowses. we might care about + # very old Python versions, but not ancient microsoft products :-) + out = os.popen("%s %s -u %s %s 2>&1" % ( + sys.executable, python_options, file, tester_options + )) + result = out.read().strip() + if result == "ok": + result = None + elif result == "skip": + print("---", "skipped") # FIXME: driver should include a reason + skipped.append(test) + continue + elif not result: + result = "(no output)" + status = out.close() + if status or result: + if status: + print("=== error", status) + if result: + if result[-3:] == "\nok": + # if there's an ok at the end, it's not really ok + result = result[:-3] + print(result) + failure = failure + 1 + else: + success = success + 1 + +print("-"*68) + +tempfiles = glob.glob(os.path.join(root, "temp_*")) +if tempfiles: + print("===", "remaining temporary files") + for file in tempfiles: + print(file) + print("-"*68) + +def tests(n): + if n == 1: + return "1 test" + else: + return "%d tests" % n + +if skipped: + print("---", tests(len(skipped)), "skipped.") + print(skipped) +if failure: + print("***", tests(failure), "of", (success + failure), "failed.") +else: + print(tests(success), "passed.") diff --git a/Tests/show_icc.py b/Tests/show_icc.py new file mode 100644 index 000000000..e062747e7 --- /dev/null +++ b/Tests/show_icc.py @@ -0,0 +1,28 @@ +import sys +sys.path.insert(0, ".") + +from PIL import Image +from PIL import ImageCms + +try: + filename = sys.argv[1] +except IndexError: + filename = "../pil-archive/cmyk.jpg" + +i = Image.open(filename) + +print(i.format) +print(i.mode) +print(i.size) +print(i.tile) + +p = ImageCms.getMemoryProfile(i.info["icc_profile"]) + +print(repr(p.product_name)) +print(repr(p.product_info)) + +o = ImageCms.createProfile("sRGB") +t = ImageCms.buildTransformFromOpenProfiles(p, o, i.mode, "RGB") +i = ImageCms.applyTransform(i, t) + +i.show() diff --git a/Tests/show_mcidas.py b/Tests/show_mcidas.py new file mode 100644 index 000000000..db193b82a --- /dev/null +++ b/Tests/show_mcidas.py @@ -0,0 +1,26 @@ +import sys +sys.path.insert(0, ".") + +from PIL import Image +from PIL import ImageMath + +try: + filename = sys.argv[1] +except IndexError: + filename = "../pil-archive/goes12.2005.140.190925.BAND_01.mcidas" + # filename = "../pil-archive/goes12.2005.140.190925.BAND_01.im" + +im = Image.open(filename) + +print(im.format) +print(im.mode) +print(im.size) +print(im.tile) + +lo, hi = im.getextrema() + +print("map", lo, hi, "->", end=' ') +im = ImageMath.eval("convert(im*255/hi, 'L')", im=im, hi=hi) +print(im.getextrema()) + +im.show() diff --git a/Tests/test_000_sanity.py b/Tests/test_000_sanity.py new file mode 100644 index 000000000..f9c34527c --- /dev/null +++ b/Tests/test_000_sanity.py @@ -0,0 +1,24 @@ +from __future__ import print_function +from tester import * + +import PIL +import PIL.Image + +# Make sure we have the binary extension +im = PIL.Image.core.new("L", (100, 100)) + +assert PIL.Image.VERSION[:3] == '1.1' + +# Create an image and do stuff with it. +im = PIL.Image.new("1", (100, 100)) +assert (im.mode, im.size) == ('1', (100, 100)) +assert len(im.tobytes() if py3 else im.tostring()) == 1300 + +# Create images in all remaining major modes. +im = PIL.Image.new("L", (100, 100)) +im = PIL.Image.new("P", (100, 100)) +im = PIL.Image.new("RGB", (100, 100)) +im = PIL.Image.new("I", (100, 100)) +im = PIL.Image.new("F", (100, 100)) + +print("ok") diff --git a/Tests/test_001_archive.py b/Tests/test_001_archive.py new file mode 100644 index 000000000..a914a6c4c --- /dev/null +++ b/Tests/test_001_archive.py @@ -0,0 +1,23 @@ +import PIL +import PIL.Image + +import glob, os + +for file in glob.glob("../pil-archive/*"): + f, e = os.path.splitext(file) + if e in [".txt", ".ttf", ".otf", ".zip"]: + continue + try: + im = PIL.Image.open(file) + im.load() + except IOError as v: + print("-", "failed to open", file, "-", v) + else: + print("+", file, im.mode, im.size, im.format) + if e == ".exif": + try: + info = im._getexif() + except KeyError as v: + print("-", "failed to get exif info from", file, "-", v) + +print("ok") diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py new file mode 100644 index 000000000..e0584641c --- /dev/null +++ b/Tests/test_file_bmp.py @@ -0,0 +1,27 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.bmp") + + lena().save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "BMP") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py new file mode 100644 index 000000000..4e06a732e --- /dev/null +++ b/Tests/test_file_fli.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.fli" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "FLI") diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py new file mode 100644 index 000000000..336aaba63 --- /dev/null +++ b/Tests/test_file_gif.py @@ -0,0 +1,28 @@ +from tester import * + +from PIL import Image + +codecs = dir(Image.core) + +if "gif_encoder" not in codecs or "gif_decoder" not in codecs: + skip("gif support not available") # can this happen? + +# sample gif stream +file = "Images/lena.gif" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "GIF") + +def test_optimize(): + def test(optimize): + im = Image.new("L", (1, 1), 0) + file = BytesIO() + im.save(file, "GIF", optimize=optimize) + return len(file.getvalue()) + assert_equal(test(0), 800) + assert_equal(test(1), 32) diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py new file mode 100644 index 000000000..f741cd663 --- /dev/null +++ b/Tests/test_file_ico.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.ico" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (16, 16)) + assert_equal(im.format, "ICO") diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py new file mode 100644 index 000000000..5246930c8 --- /dev/null +++ b/Tests/test_file_jpeg.py @@ -0,0 +1,179 @@ +from tester import * + +from PIL import Image +from PIL import ImageFile + +codecs = dir(Image.core) + +if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: + skip("jpeg support not available") + +# sample jpeg stream +file = "Images/lena.jpg" +data = open(file, "rb").read() + +def roundtrip(im, **options): + out = BytesIO() + im.save(out, "JPEG", **options) + bytes = out.tell() + out.seek(0) + im = Image.open(out) + im.bytes = bytes # for testing only + return im + +# -------------------------------------------------------------------- + +def test_sanity(): + + # internal version number + assert_match(Image.core.jpeglib_version, "\d+\.\d+$") + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "JPEG") + +# -------------------------------------------------------------------- + +def test_app(): + # Test APP/COM reader (@PIL135) + im = Image.open(file) + assert_equal(im.applist[0], + ("APP0", b"JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00")) + assert_equal(im.applist[1], ("COM", b"Python Imaging Library")) + assert_equal(len(im.applist), 2) + +def test_cmyk(): + # Test CMYK handling. Thanks to Tim and Charlie for test data, + # Michael for getting me to look one more time. + file = "Tests/images/pil_sample_cmyk.jpg" + im = Image.open(file) + # the source image has red pixels in the upper left corner. + c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] + assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0) + # the opposite corner is black + c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] + assert_true(k > 0.9) + # roundtrip, and check again + im = roundtrip(im) + c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] + assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0) + c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] + assert_true(k > 0.9) + +def test_dpi(): + def test(xdpi, ydpi=None): + im = Image.open(file) + im = roundtrip(im, dpi=(xdpi, ydpi or xdpi)) + return im.info.get("dpi") + assert_equal(test(72), (72, 72)) + assert_equal(test(300), (300, 300)) + assert_equal(test(100, 200), (100, 200)) + assert_equal(test(0), None) # square pixels + +def test_icc(): + # Test ICC support + im1 = Image.open("Tests/images/rgb.jpg") + icc_profile = im1.info["icc_profile"] + assert_equal(len(icc_profile), 3144) + # Roundtrip via physical file. + file = tempfile("temp.jpg") + im1.save(file, icc_profile=icc_profile) + im2 = Image.open(file) + assert_equal(im2.info.get("icc_profile"), icc_profile) + # Roundtrip via memory buffer. + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), icc_profile=icc_profile) + assert_image_equal(im1, im2) + assert_false(im1.info.get("icc_profile")) + assert_true(im2.info.get("icc_profile")) + +def test_icc_big(): + # Make sure that the "extra" support handles large blocks + def test(n): + # The ICC APP marker can store 65519 bytes per marker, so + # using a 4-byte test code should allow us to detect out of + # order issues. + icc_profile = (b"Test"*int(n/4+1))[:n] + assert len(icc_profile) == n # sanity + im1 = roundtrip(lena(), icc_profile=icc_profile) + assert_equal(im1.info.get("icc_profile"), icc_profile or None) + test(0); test(1) + test(3); test(4); test(5) + test(65533-14) # full JPEG marker block + test(65533-14+1) # full block plus one byte + test(ImageFile.MAXBLOCK) # full buffer block + test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte + test(ImageFile.MAXBLOCK*4+3) # large block + +def test_optimize(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), optimize=1) + assert_image_equal(im1, im2) + assert_true(im1.bytes >= im2.bytes) + +def test_progressive(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), progressive=1) + im3 = roundtrip(lena(), progression=1) # compatibility + assert_image_equal(im1, im2) + assert_image_equal(im1, im3) + assert_false(im1.info.get("progressive")) + assert_false(im1.info.get("progression")) + assert_true(im2.info.get("progressive")) + assert_true(im2.info.get("progression")) + assert_true(im3.info.get("progressive")) + assert_true(im3.info.get("progression")) + +def test_quality(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), quality=50) + assert_image(im1, im2.mode, im2.size) + assert_true(im1.bytes >= im2.bytes) + +def test_smooth(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), smooth=100) + assert_image(im1, im2.mode, im2.size) + +def test_subsampling(): + def getsampling(im): + layer = im.layer + return layer[0][1:3] + layer[1][1:3] + layer[2][1:3] + # experimental API + im = roundtrip(lena(), subsampling=-1) # default + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=0) # 4:4:4 + assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=1) # 4:2:2 + assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=2) # 4:1:1 + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=3) # default (undefined) + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + + im = roundtrip(lena(), subsampling="4:4:4") + assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling="4:2:2") + assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling="4:1:1") + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + + assert_exception(TypeError, lambda: roundtrip(lena(), subsampling="1:1:1")) + +def test_truncated_jpeg(): + def test(junk): + if junk: + # replace "junk" bytes at the end with junk + file = BytesIO(data[:-junk] + b'\0'*junk) + else: + file = BytesIO(data) + im = Image.open(file) + im.load() + assert_no_exception(lambda: test(0)) + assert_exception(IOError, lambda: test(1)) + assert_no_exception(lambda: test(2)) + assert_no_exception(lambda: test(4)) + assert_no_exception(lambda: test(8)) + assert_exception(IOError, lambda: test(10)) diff --git a/Tests/test_file_msp.py b/Tests/test_file_msp.py new file mode 100644 index 000000000..7ed200e43 --- /dev/null +++ b/Tests/test_file_msp.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.msp") + + lena("1").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "1") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "MSP") diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py new file mode 100644 index 000000000..73d358229 --- /dev/null +++ b/Tests/test_file_pcx.py @@ -0,0 +1,39 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.pcx") + + lena("1").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "1") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PCX") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) + +def test_pil184(): + # Check reading of files where xmin/xmax is not zero. + + file = "Tests/images/pil184.pcx" + im = Image.open(file) + + assert_equal(im.size, (447, 144)) + assert_equal(im.tile[0][1], (0, 0, 447, 144)) + + # Make sure all pixels are either 0 or 255. + assert_equal(im.histogram()[0] + im.histogram()[255], 447*144) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py new file mode 100644 index 000000000..7086d8709 --- /dev/null +++ b/Tests/test_file_png.py @@ -0,0 +1,176 @@ +from tester import * + +from PIL import Image +from PIL import PngImagePlugin + +codecs = dir(Image.core) + +if "zip_encoder" not in codecs or "zip_decoder" not in codecs: + skip("zip/deflate support not available") + +# sample png stream + +file = "Images/lena.png" +data = open(file, "rb").read() + +# stuff to create inline PNG images + +MAGIC = PngImagePlugin._MAGIC + +def chunk(cid, *data): + file = BytesIO() + PngImagePlugin.putchunk(*(file, cid) + data) + return file.getvalue() + +o32 = PngImagePlugin.o32 + +IHDR = chunk(b"IHDR", o32(1), o32(1), b'\x08\x02', b'\0\0\0') +IDAT = chunk(b"IDAT") +IEND = chunk(b"IEND") + +HEAD = MAGIC + IHDR +TAIL = IDAT + IEND + +def load(data): + return Image.open(BytesIO(data)) + +def roundtrip(im, **options): + out = BytesIO() + im.save(out, "PNG", **options) + out.seek(0) + return Image.open(out) + +# -------------------------------------------------------------------- + +def test_sanity(): + + # internal version number + assert_match(Image.core.zlib_version, "\d+\.\d+\.\d+(\.\d+)?$") + + file = tempfile("temp.png") + + lena("RGB").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PNG") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) + + lena("I").save(file) + im = Image.open(file) + +# -------------------------------------------------------------------- + +def test_broken(): + # Check reading of totally broken files. In this case, the test + # file was checked into Subversion as a text file. + + file = "Tests/images/broken.png" + assert_exception(IOError, lambda: Image.open(file)) + +def test_bad_text(): + # Make sure PIL can read malformed tEXt chunks (@PIL152) + + im = load(HEAD + chunk(b'tEXt') + TAIL) + assert_equal(im.info, {}) + + im = load(HEAD + chunk(b'tEXt', b'spam') + TAIL) + assert_equal(im.info, {'spam': ''}) + + im = load(HEAD + chunk(b'tEXt', b'spam\0') + TAIL) + assert_equal(im.info, {'spam': ''}) + + im = load(HEAD + chunk(b'tEXt', b'spam\0egg') + TAIL) + assert_equal(im.info, {'spam': 'egg'}) + + im = load(HEAD + chunk(b'tEXt', b'spam\0egg\0') + TAIL) + assert_equal(im.info, {'spam': 'egg\x00'}) + +def test_interlace(): + + file = "Tests/images/pil123p.png" + im = Image.open(file) + + assert_image(im, "P", (162, 150)) + assert_true(im.info.get("interlace")) + + assert_no_exception(lambda: im.load()) + + file = "Tests/images/pil123rgba.png" + im = Image.open(file) + + assert_image(im, "RGBA", (162, 150)) + assert_true(im.info.get("interlace")) + + assert_no_exception(lambda: im.load()) + +def test_load_verify(): + # Check open/load/verify exception (@PIL150) + + im = Image.open("Images/lena.png") + assert_no_exception(lambda: im.verify()) + + im = Image.open("Images/lena.png") + im.load() + assert_exception(RuntimeError, lambda: im.verify()) + +def test_roundtrip_dpi(): + # Check dpi roundtripping + + im = Image.open(file) + + im = roundtrip(im, dpi=(100, 100)) + assert_equal(im.info["dpi"], (100, 100)) + +def test_roundtrip_text(): + # Check text roundtripping + + im = Image.open(file) + + info = PngImagePlugin.PngInfo() + info.add_text("TXT", "VALUE") + info.add_text("ZIP", "VALUE", 1) + + im = roundtrip(im, pnginfo=info) + assert_equal(im.info, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) + assert_equal(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) + +def test_scary(): + # Check reading of evil PNG file. For information, see: + # http://scary.beasts.org/security/CESA-2004-001.txt + + import base64 + file = "Tests/images/pngtest_bad.png.base64" + data = None + + if py3: + data = base64.decodebytes(open(file, 'rb').read()) + else: + data = base64.decodestring(open(file, 'rb').read()) + + file = BytesIO(data) + assert_exception(IOError, lambda: Image.open(file)) + +def test_trns_rgb(): + # Check writing and reading of tRNS chunks for RGB images. + # Independent file sample provided by Sebastian Spaeth. + + file = "Tests/images/caption_6_33_22.png" + im = Image.open(file) + assert_equal(im.info["transparency"], (248, 248, 248)) + + im = roundtrip(im, transparency=(0, 1, 2)) + assert_equal(im.info["transparency"], (0, 1, 2)) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py new file mode 100644 index 000000000..fccb94905 --- /dev/null +++ b/Tests/test_file_ppm.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.ppm" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PPM") diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py new file mode 100644 index 000000000..ef2d40594 --- /dev/null +++ b/Tests/test_file_psd.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.psd" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PSD") diff --git a/Tests/test_file_tar.py b/Tests/test_file_tar.py new file mode 100644 index 000000000..0f87ea2c0 --- /dev/null +++ b/Tests/test_file_tar.py @@ -0,0 +1,22 @@ +from tester import * + +from PIL import Image, TarIO + +# sample ppm stream +tarfile = "Images/lena.tar" + +def test_sanity(): + tar = TarIO.TarIO(tarfile, 'lena.png') + im = Image.open(tar) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PNG") + + tar = TarIO.TarIO(tarfile, 'lena.jpg') + im = Image.open(tar) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "JPEG") + diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py new file mode 100644 index 000000000..843e13d28 --- /dev/null +++ b/Tests/test_file_tiff.py @@ -0,0 +1,57 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.tif") + + lena("RGB").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "TIFF") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) + + lena("I").save(file) + im = Image.open(file) + +def test_mac_tiff(): + # Read RGBa images from Mac OS X [@PIL136] + + file = "Tests/images/pil136.tiff" + im = Image.open(file) + + assert_equal(im.mode, "RGBA") + assert_equal(im.size, (55, 43)) + assert_equal(im.tile, [('raw', (0, 0, 55, 43), 8, ('RGBa', 0, 1))]) + assert_no_exception(lambda: im.load()) + +def test_gimp_tiff(): + # Read TIFF JPEG images from GIMP [@PIL168] + + file = "Tests/images/pil168.tif" + im = Image.open(file) + + assert_equal(im.mode, "RGB") + assert_equal(im.size, (256, 256)) + assert_equal(im.tile, [ + ('jpeg', (0, 0, 256, 64), 8, ('RGB', '')), + ('jpeg', (0, 64, 256, 128), 1215, ('RGB', '')), + ('jpeg', (0, 128, 256, 192), 2550, ('RGB', '')), + ('jpeg', (0, 192, 256, 256), 3890, ('RGB', '')), + ]) + assert_no_exception(lambda: im.load()) diff --git a/Tests/test_file_xbm.py b/Tests/test_file_xbm.py new file mode 100644 index 000000000..f27a3a349 --- /dev/null +++ b/Tests/test_file_xbm.py @@ -0,0 +1,34 @@ +from tester import * + +from PIL import Image + +PIL151 = b""" +#define basic_width 32 +#define basic_height 32 +static char basic_bits[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, +0x80, 0xff, 0xff, 0x01, 0x40, 0x00, 0x00, 0x02, +0x20, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x08, +0x10, 0x00, 0x00, 0x08, +0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08, +0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08, +0x20, 0x00, 0x00, 0x04, +0x20, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x02, +0x80, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, +}; +""" + +def test_pil151(): + + im = Image.open(BytesIO(PIL151)) + + assert_no_exception(lambda: im.load()) + assert_equal(im.mode, '1') + assert_equal(im.size, (32, 32)) diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py new file mode 100644 index 000000000..44135d028 --- /dev/null +++ b/Tests/test_file_xpm.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.xpm" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "XPM") diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py new file mode 100644 index 000000000..366bb4468 --- /dev/null +++ b/Tests/test_font_bdf.py @@ -0,0 +1,13 @@ +from tester import * + +from PIL import Image, FontFile, BdfFontFile + +filename = "Images/courB08.bdf" + +def test_sanity(): + + file = open(filename, "rb") + font = BdfFontFile.BdfFontFile(file) + + assert_true(isinstance(font, FontFile.FontFile)) + assert_equal(len([_f for _f in font.glyph if _f]), 190) diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py new file mode 100644 index 000000000..958657eaa --- /dev/null +++ b/Tests/test_font_pcf.py @@ -0,0 +1,26 @@ +from tester import * + +from PIL import Image, FontFile, PcfFontFile +from PIL import ImageFont, ImageDraw + +fontname = "Tests/fonts/helvO18.pcf" +tempname = tempfile("temp.pil", "temp.pbm") + +message = "hello, world" + +def test_sanity(): + + file = open(fontname, "rb") + font = PcfFontFile.PcfFontFile(file) + assert_true(isinstance(font, FontFile.FontFile)) + assert_equal(len([_f for _f in font.glyph if _f]), 192) + + font.save(tempname) + +def test_draw(): + + font = ImageFont.load(tempname) + image = Image.new("L", font.getsize(message), "white") + draw = ImageDraw.Draw(image) + draw.text((0, 0), message, font=font) + # assert_signature(image, "7216c60f988dea43a46bb68321e3c1b03ec62aee") diff --git a/Tests/test_image.py b/Tests/test_image.py new file mode 100644 index 000000000..cb1de08d5 --- /dev/null +++ b/Tests/test_image.py @@ -0,0 +1,39 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = Image.new("L", (100, 100)) + assert_equal(repr(im)[:45], " 2**32: + assert_equal(put(sys.maxsize), (255, 255, 255, 255)) + else: + assert_equal(put(sys.maxsize), (255, 255, 255, 127)) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py new file mode 100644 index 000000000..b7ebb8853 --- /dev/null +++ b/Tests/test_image_putpalette.py @@ -0,0 +1,28 @@ +from tester import * + +from PIL import Image +from PIL import ImagePalette + +def test_putpalette(): + def palette(mode): + im = lena(mode).copy() + im.putpalette(list(range(256))*3) + p = im.getpalette() + if p: + return im.mode, p[:10] + return im.mode + assert_exception(ValueError, lambda: palette("1")) + assert_equal(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + assert_equal(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + assert_exception(ValueError, lambda: palette("I")) + assert_exception(ValueError, lambda: palette("F")) + assert_exception(ValueError, lambda: palette("RGB")) + assert_exception(ValueError, lambda: palette("RGBA")) + assert_exception(ValueError, lambda: palette("YCbCr")) + +def test_imagepalette(): + im = lena("P") + assert_no_exception(lambda: im.putpalette(ImagePalette.negative())) + assert_no_exception(lambda: im.putpalette(ImagePalette.random())) + assert_no_exception(lambda: im.putpalette(ImagePalette.sepia())) + assert_no_exception(lambda: im.putpalette(ImagePalette.wedge())) diff --git a/Tests/test_image_putpixel.py b/Tests/test_image_putpixel.py new file mode 100644 index 000000000..555a92f74 --- /dev/null +++ b/Tests/test_image_putpixel.py @@ -0,0 +1,43 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im1 = lena() + im2 = Image.new(im1.mode, im1.size, 0) + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + im2.readonly = 1 + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + assert_false(im2.readonly) + assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + + pix1 = im1.load() + pix2 = im2.load() + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pix2[x, y] = pix1[x, y] + + assert_image_equal(im1, im2) + + + + +# see test_image_getpixel for more tests + diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py new file mode 100644 index 000000000..5dcbab7fe --- /dev/null +++ b/Tests/test_image_quantize.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = lena() + + im = im.quantize() + assert_image(im, "P", im.size) + + im = lena() + im = im.quantize(palette=lena("P")) + assert_image(im, "P", im.size) + diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py new file mode 100644 index 000000000..4e228a396 --- /dev/null +++ b/Tests/test_image_resize.py @@ -0,0 +1,12 @@ +from tester import * + +from PIL import Image + +def test_resize(): + def resize(mode, size): + out = lena(mode).resize(size) + assert_equal(out.mode, mode) + assert_equal(out.size, size) + for mode in "1", "P", "L", "RGB", "I", "F": + yield_test(resize, mode, (100, 100)) + yield_test(resize, mode, (200, 200)) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py new file mode 100644 index 000000000..5e4782c87 --- /dev/null +++ b/Tests/test_image_rotate.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_rotate(): + def rotate(mode): + im = lena(mode) + out = im.rotate(45) + assert_equal(out.mode, mode) + assert_equal(out.size, im.size) # default rotate clips output + out = im.rotate(45, expand=1) + assert_equal(out.mode, mode) + assert_true(out.size != im.size) + for mode in "1", "P", "L", "RGB", "I", "F": + yield_test(rotate, mode) diff --git a/Tests/test_image_save.py b/Tests/test_image_save.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_save.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_seek.py b/Tests/test_image_seek.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_seek.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_show.py b/Tests/test_image_show.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_show.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_split.py b/Tests/test_image_split.py new file mode 100644 index 000000000..7fc70182b --- /dev/null +++ b/Tests/test_image_split.py @@ -0,0 +1,42 @@ +from tester import * + +from PIL import Image + +def test_split(): + def split(mode): + layers = lena(mode).split() + return [(i.mode, i.size[0], i.size[1]) for i in layers] + assert_equal(split("1"), [('1', 128, 128)]) + assert_equal(split("L"), [('L', 128, 128)]) + assert_equal(split("I"), [('I', 128, 128)]) + assert_equal(split("F"), [('F', 128, 128)]) + assert_equal(split("P"), [('P', 128, 128)]) + assert_equal(split("RGB"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + assert_equal(split("RGBA"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + assert_equal(split("CMYK"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + assert_equal(split("YCbCr"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + +def test_split_merge(): + def split_merge(mode): + return Image.merge(mode, lena(mode).split()) + assert_image_equal(lena("1"), split_merge("1")) + assert_image_equal(lena("L"), split_merge("L")) + assert_image_equal(lena("I"), split_merge("I")) + assert_image_equal(lena("F"), split_merge("F")) + assert_image_equal(lena("P"), split_merge("P")) + assert_image_equal(lena("RGB"), split_merge("RGB")) + assert_image_equal(lena("RGBA"), split_merge("RGBA")) + assert_image_equal(lena("CMYK"), split_merge("CMYK")) + assert_image_equal(lena("YCbCr"), split_merge("YCbCr")) + +def test_split_open(): + file = tempfile("temp.png") + def split_open(mode): + lena(mode).save(file) + im = Image.open(file) + return len(im.split()) + assert_equal(split_open("1"), 1) + assert_equal(split_open("L"), 1) + assert_equal(split_open("P"), 1) + assert_equal(split_open("RGB"), 3) + assert_equal(split_open("RGBA"), 4) diff --git a/Tests/test_image_tell.py b/Tests/test_image_tell.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_tell.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py new file mode 100644 index 000000000..871dd1f54 --- /dev/null +++ b/Tests/test_image_thumbnail.py @@ -0,0 +1,36 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = lena() + im.thumbnail((100, 100)) + + assert_image(im, im.mode, (100, 100)) + +def test_aspect(): + + im = lena() + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 100)) + + im = lena().resize((128, 256)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (50, 100)) + + im = lena().resize((128, 256)) + im.thumbnail((50, 100)) + assert_image(im, im.mode, (50, 100)) + + im = lena().resize((256, 128)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 50)) + + im = lena().resize((256, 128)) + im.thumbnail((100, 50)) + assert_image(im, im.mode, (100, 50)) + + im = lena().resize((128, 128)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 100)) diff --git a/Tests/test_image_tobitmap.py b/Tests/test_image_tobitmap.py new file mode 100644 index 000000000..f8186ae14 --- /dev/null +++ b/Tests/test_image_tobitmap.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + assert_exception(ValueError, lambda: lena().tobitmap()) + assert_no_exception(lambda: lena().convert("1").tobitmap()) + + im1 = lena().convert("1") + + bitmap = im1.tobitmap() + + assert_true(isinstance(bitmap, bytes)) + assert_image_equal(im1, fromstring(bitmap)) diff --git a/Tests/test_image_tostring.py b/Tests/test_image_tostring.py new file mode 100644 index 000000000..c4d89df93 --- /dev/null +++ b/Tests/test_image_tostring.py @@ -0,0 +1,7 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + data = lena().tobytes() if py3 else lena().tostring() + assert_true(isinstance(data, bytes)) diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_transform.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py new file mode 100644 index 000000000..010478df4 --- /dev/null +++ b/Tests/test_image_transpose.py @@ -0,0 +1,34 @@ +from tester import * + +from PIL import Image + +FLIP_LEFT_RIGHT = Image.FLIP_LEFT_RIGHT +FLIP_TOP_BOTTOM = Image.FLIP_TOP_BOTTOM +ROTATE_90 = Image.ROTATE_90 +ROTATE_180 = Image.ROTATE_180 +ROTATE_270 = Image.ROTATE_270 + +def test_sanity(): + + im = lena() + + assert_no_exception(lambda: im.transpose(FLIP_LEFT_RIGHT)) + assert_no_exception(lambda: im.transpose(FLIP_TOP_BOTTOM)) + + assert_no_exception(lambda: im.transpose(ROTATE_90)) + assert_no_exception(lambda: im.transpose(ROTATE_180)) + assert_no_exception(lambda: im.transpose(ROTATE_270)) + +def test_roundtrip(): + + im = lena() + + def transpose(first, second): + return im.transpose(first).transpose(second) + + assert_image_equal(im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) + assert_image_equal(im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) + + assert_image_equal(im, transpose(ROTATE_90, ROTATE_270)) + assert_image_equal(im, transpose(ROTATE_180, ROTATE_180)) + diff --git a/Tests/test_image_verify.py b/Tests/test_image_verify.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_verify.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py new file mode 100644 index 000000000..16eaaf55e --- /dev/null +++ b/Tests/test_imagechops.py @@ -0,0 +1,56 @@ +from tester import * + +from PIL import Image +from PIL import ImageChops + +def test_sanity(): + + im = lena("L") + + ImageChops.constant(im, 128) + ImageChops.duplicate(im) + ImageChops.invert(im) + ImageChops.lighter(im, im) + ImageChops.darker(im, im) + ImageChops.difference(im, im) + ImageChops.multiply(im, im) + ImageChops.screen(im, im) + + ImageChops.add(im, im) + ImageChops.add(im, im, 2.0) + ImageChops.add(im, im, 2.0, 128) + ImageChops.subtract(im, im) + ImageChops.subtract(im, im, 2.0) + ImageChops.subtract(im, im, 2.0, 128) + + ImageChops.add_modulo(im, im) + ImageChops.subtract_modulo(im, im) + + ImageChops.blend(im, im, 0.5) + ImageChops.composite(im, im, im) + + ImageChops.offset(im, 10) + ImageChops.offset(im, 10, 20) + +def test_logical(): + + def table(op, a, b): + out = [] + for x in (a, b): + imx = Image.new("1", (1, 1), x) + for y in (a, b): + imy = Image.new("1", (1, 1), y) + out.append(op(imx, imy).getpixel((0, 0))) + return tuple(out) + + assert_equal(table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) + + assert_equal(table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) + + assert_equal(table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py new file mode 100644 index 000000000..29e578192 --- /dev/null +++ b/Tests/test_imagecms.py @@ -0,0 +1,82 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageCms +except ImportError: + skip() + +SRGB = "Tests/icc/sRGB.icm" + +def test_sanity(): + + # basic smoke test. + # this mostly follows the cms_test outline. + + v = ImageCms.versions() # should return four strings + assert_equal(v[0], '0.1.0 pil') + assert_equal(list(map(type, v)), [str, str, str, str]) + + # internal version number + assert_match(ImageCms.core.littlecms_version, "\d+\.\d+$") + + i = ImageCms.profileToProfile(lena(), SRGB, SRGB) + assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + p = ImageCms.createProfile("sRGB") + o = ImageCms.getOpenProfile(SRGB) + t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") + assert_equal(t.inputMode, "RGB") + assert_equal(t.outputMode, "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + # get profile information for file + assert_equal(ImageCms.getProfileName(SRGB).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(), + ['sRGB IEC61966-2.1', '', + 'Copyright (c) 1998 Hewlett-Packard Company', '', + 'WhitePoint : D65 (daylight)', '', + 'Tests/icc/sRGB.icm']) + assert_equal(ImageCms.getDefaultIntent(SRGB), 0) + assert_equal(ImageCms.isIntentSupported( + SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + + # same, using profile object + p = ImageCms.createProfile("sRGB") + assert_equal(ImageCms.getProfileName(p).strip(), + 'sRGB built-in - (lcms internal)') + assert_equal(ImageCms.getProfileInfo(p).splitlines(), + ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) + assert_equal(ImageCms.getDefaultIntent(p), 0) + assert_equal(ImageCms.isIntentSupported( + p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + + # extensions + i = Image.open("Tests/images/rgb.jpg") + p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) + assert_equal(ImageCms.getProfileName(p).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + # the procedural pyCMS API uses PyCMSError for all sorts of errors + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None)) + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None)) + + # test PointTransform convenience API + im = lena().point(t) + + # try fetching the profile for the current display device + assert_no_exception(lambda: ImageCms.get_display_profile()) diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py new file mode 100644 index 000000000..534005fe4 --- /dev/null +++ b/Tests/test_imagecolor.py @@ -0,0 +1,31 @@ +from tester import * + +from PIL import Image +from PIL import ImageColor + +# -------------------------------------------------------------------- +# sanity + +assert_equal((255, 0, 0), ImageColor.getrgb("#f00")) +assert_equal((255, 0, 0), ImageColor.getrgb("#ff0000")) +assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)")) +assert_equal((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)")) +assert_equal((255, 0, 0), ImageColor.getrgb("hsl(0, 100%, 50%)")) +assert_equal((255, 0, 0), ImageColor.getrgb("red")) + +# -------------------------------------------------------------------- +# look for rounding errors (based on code by Tim Hatch) + +for color in list(ImageColor.colormap.keys()): + expected = Image.new("RGB", (1, 1), color).convert("L").getpixel((0, 0)) + actual = Image.new("L", (1, 1), color).getpixel((0, 0)) + assert_equal(expected, actual) + +assert_equal((0, 0, 0), ImageColor.getcolor("black", "RGB")) +assert_equal((255, 255, 255), ImageColor.getcolor("white", "RGB")) + +assert_equal(0, ImageColor.getcolor("black", "L")) +assert_equal(255, ImageColor.getcolor("white", "L")) + +assert_equal(0, ImageColor.getcolor("black", "1")) +assert_equal(255, ImageColor.getcolor("white", "1")) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py new file mode 100644 index 000000000..c6fd20f52 --- /dev/null +++ b/Tests/test_imagedraw.py @@ -0,0 +1,28 @@ +from tester import * + +from PIL import Image +from PIL import ImageDraw + +def test_sanity(): + + im = lena("RGB").copy() + + draw = ImageDraw.ImageDraw(im) + draw = ImageDraw.Draw(im) + + draw.ellipse(list(range(4))) + draw.line(list(range(10))) + draw.polygon(list(range(100))) + draw.rectangle(list(range(4))) + + success() + +def test_deprecated(): + + im = lena().copy() + + draw = ImageDraw.Draw(im) + + assert_warning(DeprecationWarning, lambda: draw.setink(0)) + assert_warning(DeprecationWarning, lambda: draw.setfill(0)) + diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py new file mode 100644 index 000000000..04f16bfa5 --- /dev/null +++ b/Tests/test_imageenhance.py @@ -0,0 +1,19 @@ +from tester import * + +from PIL import Image +from PIL import ImageEnhance + +def test_sanity(): + + # FIXME: assert_image + assert_no_exception(lambda: ImageEnhance.Color(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Contrast(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Brightness(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Sharpness(lena()).enhance(0.5)) + +def test_crash(): + + # crashes on small images + im = Image.new("RGB", (1, 1)) + assert_no_exception(lambda: ImageEnhance.Sharpness(im).enhance(0.5)) + diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py new file mode 100644 index 000000000..95ed0ee30 --- /dev/null +++ b/Tests/test_imagefile.py @@ -0,0 +1,63 @@ +from tester import * + +from PIL import Image +from PIL import ImageFile + +# save original block sizes +MAXBLOCK = ImageFile.MAXBLOCK +SAFEBLOCK = ImageFile.SAFEBLOCK + +def test_parser(): + + def roundtrip(format): + + im = lena("L").resize((1000, 1000)) + if format in ("MSP", "XBM"): + im = im.convert("1") + + file = BytesIO() + + im.save(file, format) + + data = file.getvalue() + + parser = ImageFile.Parser() + parser.feed(data) + imOut = parser.close() + + return im, imOut + + assert_image_equal(*roundtrip("BMP")) + assert_image_equal(*roundtrip("GIF")) + assert_image_equal(*roundtrip("IM")) + assert_image_equal(*roundtrip("MSP")) + try: + # force multiple blocks in PNG driver + ImageFile.MAXBLOCK = 8192 + assert_image_equal(*roundtrip("PNG")) + finally: + ImageFile.MAXBLOCK = MAXBLOCK + assert_image_equal(*roundtrip("PPM")) + assert_image_equal(*roundtrip("TIFF")) + assert_image_equal(*roundtrip("XBM")) + #assert_image_equal(*roundtrip("EPS")) #no eps_decoder + assert_image_equal(*roundtrip("TGA")) + assert_image_equal(*roundtrip("PCX")) + + im1, im2 = roundtrip("JPEG") # lossy compression + assert_image(im1, im2.mode, im2.size) + + assert_exception(IOError, lambda: roundtrip("PDF")) + + +def test_safeblock(): + + im1 = lena() + + try: + ImageFile.SAFEBLOCK = 1 + im2 = fromstring(tostring(im1, "PNG")) + finally: + ImageFile.SAFEBLOCK = SAFEBLOCK + + assert_image_equal(im1, im2) diff --git a/Tests/test_imagefileio.py b/Tests/test_imagefileio.py new file mode 100644 index 000000000..259c6fe1c --- /dev/null +++ b/Tests/test_imagefileio.py @@ -0,0 +1,24 @@ +from tester import * + +from PIL import Image +from PIL import ImageFileIO + +def test_fileio(): + + class DumbFile: + def __init__(self, data): + self.data = data + def read(self, bytes=None): + assert_equal(bytes, None) + return self.data + def close(self): + pass + + im1 = lena() + + io = ImageFileIO.ImageFileIO(DumbFile(tostring(im1, "PPM"))) + + im2 = Image.open(io) + assert_image_equal(im1, im2) + + diff --git a/Tests/test_imagefilter.py b/Tests/test_imagefilter.py new file mode 100644 index 000000000..214f88024 --- /dev/null +++ b/Tests/test_imagefilter.py @@ -0,0 +1,31 @@ +from tester import * + +from PIL import Image +from PIL import ImageFilter + +def test_sanity(): + # see test_image_filter for more tests + + assert_no_exception(lambda: ImageFilter.MaxFilter) + assert_no_exception(lambda: ImageFilter.MedianFilter) + assert_no_exception(lambda: ImageFilter.MinFilter) + assert_no_exception(lambda: ImageFilter.ModeFilter) + assert_no_exception(lambda: ImageFilter.Kernel((3, 3), list(range(9)))) + assert_no_exception(lambda: ImageFilter.GaussianBlur) + assert_no_exception(lambda: ImageFilter.GaussianBlur(5)) + assert_no_exception(lambda: ImageFilter.UnsharpMask) + assert_no_exception(lambda: ImageFilter.UnsharpMask(10)) + + assert_no_exception(lambda: ImageFilter.BLUR) + assert_no_exception(lambda: ImageFilter.CONTOUR) + assert_no_exception(lambda: ImageFilter.DETAIL) + assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE) + assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE_MORE) + assert_no_exception(lambda: ImageFilter.EMBOSS) + assert_no_exception(lambda: ImageFilter.FIND_EDGES) + assert_no_exception(lambda: ImageFilter.SMOOTH) + assert_no_exception(lambda: ImageFilter.SMOOTH_MORE) + assert_no_exception(lambda: ImageFilter.SHARPEN) + + + diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py new file mode 100644 index 000000000..3c4e1f1b8 --- /dev/null +++ b/Tests/test_imagefont.py @@ -0,0 +1,12 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageFont + ImageFont.core.getfont # check if freetype is available +except ImportError: + skip() + +def test_sanity(): + + assert_match(ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") diff --git a/Tests/test_imagegl.py b/Tests/test_imagegl.py new file mode 100644 index 000000000..87dd6aa2d --- /dev/null +++ b/Tests/test_imagegl.py @@ -0,0 +1,9 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageGL +except ImportError as v: + skip(v) + +success() diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py new file mode 100644 index 000000000..67ff71960 --- /dev/null +++ b/Tests/test_imagegrab.py @@ -0,0 +1,13 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageGrab +except ImportError as v: + skip(v) + +def test_grab(): + im = ImageGrab.grab() + assert_image(im, im.mode, im.size) + + diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py new file mode 100644 index 000000000..eaeb711ba --- /dev/null +++ b/Tests/test_imagemath.py @@ -0,0 +1,62 @@ +from tester import * + +from PIL import Image +from PIL import ImageMath + +def pixel(im): + if hasattr(im, "im"): + return "%s %r" % (im.mode, im.getpixel((0, 0))) + else: + if isinstance(im, type(0)): + return int(im) # hack to deal with booleans + print(im) + +A = Image.new("L", (1, 1), 1) +B = Image.new("L", (1, 1), 2) +F = Image.new("F", (1, 1), 3) +I = Image.new("I", (1, 1), 4) + +images = {"A": A, "B": B, "F": F, "I": I} + +def test_sanity(): + assert_equal(ImageMath.eval("1"), 1) + assert_equal(ImageMath.eval("1+A", A=2), 3) + assert_equal(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") + assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") + assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + assert_equal(pixel(ImageMath.eval("int(float(A)+B)", images)), "I 3") + +def test_ops(): + + assert_equal(pixel(ImageMath.eval("-A", images)), "I -1") + assert_equal(pixel(ImageMath.eval("+B", images)), "L 2") + + assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") + assert_equal(pixel(ImageMath.eval("A-B", images)), "I -1") + assert_equal(pixel(ImageMath.eval("A*B", images)), "I 2") + assert_equal(pixel(ImageMath.eval("A/B", images)), "I 0") + assert_equal(pixel(ImageMath.eval("B**2", images)), "I 4") + assert_equal(pixel(ImageMath.eval("B**33", images)), "I 2147483647") + + assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + assert_equal(pixel(ImageMath.eval("float(A)-B", images)), "F -1.0") + assert_equal(pixel(ImageMath.eval("float(A)*B", images)), "F 2.0") + assert_equal(pixel(ImageMath.eval("float(A)/B", images)), "F 0.5") + assert_equal(pixel(ImageMath.eval("float(B)**2", images)), "F 4.0") + assert_equal(pixel(ImageMath.eval("float(B)**33", images)), "F 8589934592.0") + +def test_logical(): + assert_equal(pixel(ImageMath.eval("not A", images)), 0) + assert_equal(pixel(ImageMath.eval("A and B", images)), "L 2") + assert_equal(pixel(ImageMath.eval("A or B", images)), "L 1") + +def test_convert(): + assert_equal(pixel(ImageMath.eval("convert(A+B, 'L')", images)), "L 3") + assert_equal(pixel(ImageMath.eval("convert(A+B, '1')", images)), "1 0") + assert_equal(pixel(ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") + +def test_compare(): + assert_equal(pixel(ImageMath.eval("min(A, B)", images)), "I 1") + assert_equal(pixel(ImageMath.eval("max(A, B)", images)), "I 2") + assert_equal(pixel(ImageMath.eval("A == 1", images)), "I 1") + assert_equal(pixel(ImageMath.eval("A == 2", images)), "I 0") diff --git a/Tests/test_imagemode.py b/Tests/test_imagemode.py new file mode 100644 index 000000000..54b04435f --- /dev/null +++ b/Tests/test_imagemode.py @@ -0,0 +1,23 @@ +from tester import * + +from PIL import Image +from PIL import ImageMode + +ImageMode.getmode("1") +ImageMode.getmode("L") +ImageMode.getmode("P") +ImageMode.getmode("RGB") +ImageMode.getmode("I") +ImageMode.getmode("F") + +m = ImageMode.getmode("1") +assert_equal(m.mode, "1") +assert_equal(m.bands, ("1",)) +assert_equal(m.basemode, "L") +assert_equal(m.basetype, "L") + +m = ImageMode.getmode("RGB") +assert_equal(m.mode, "RGB") +assert_equal(m.bands, ("R", "G", "B")) +assert_equal(m.basemode, "RGB") +assert_equal(m.basetype, "L") diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py new file mode 100644 index 000000000..362937e81 --- /dev/null +++ b/Tests/test_imageops.py @@ -0,0 +1,70 @@ +from tester import * + +from PIL import Image +from PIL import ImageOps + +class Deformer: + def getmesh(self, im): + x, y = im.size + return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] + +deformer = Deformer() + +def test_sanity(): + + ImageOps.autocontrast(lena("L")) + ImageOps.autocontrast(lena("RGB")) + + ImageOps.autocontrast(lena("L"), cutoff=10) + ImageOps.autocontrast(lena("L"), ignore=[0, 255]) + + ImageOps.colorize(lena("L"), (0, 0, 0), (255, 255, 255)) + ImageOps.colorize(lena("L"), "black", "white") + + ImageOps.crop(lena("L"), 1) + ImageOps.crop(lena("RGB"), 1) + + ImageOps.deform(lena("L"), deformer) + ImageOps.deform(lena("RGB"), deformer) + + ImageOps.equalize(lena("L")) + ImageOps.equalize(lena("RGB")) + + ImageOps.expand(lena("L"), 1) + ImageOps.expand(lena("RGB"), 1) + ImageOps.expand(lena("L"), 2, "blue") + ImageOps.expand(lena("RGB"), 2, "blue") + + ImageOps.fit(lena("L"), (128, 128)) + ImageOps.fit(lena("RGB"), (128, 128)) + + ImageOps.flip(lena("L")) + ImageOps.flip(lena("RGB")) + + ImageOps.grayscale(lena("L")) + ImageOps.grayscale(lena("RGB")) + + ImageOps.invert(lena("L")) + ImageOps.invert(lena("RGB")) + + ImageOps.mirror(lena("L")) + ImageOps.mirror(lena("RGB")) + + ImageOps.posterize(lena("L"), 4) + ImageOps.posterize(lena("RGB"), 4) + + ImageOps.solarize(lena("L")) + ImageOps.solarize(lena("RGB")) + + success() + +def test_pil163(): + # Division by zero in equalize if < 255 pixels in image (@PIL163) + + i = lena("RGB").resize((15, 16)) + + ImageOps.equalize(i.convert("L")) + ImageOps.equalize(i.convert("P")) + ImageOps.equalize(i.convert("RGB")) + + success() diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py new file mode 100644 index 000000000..83a93b8e0 --- /dev/null +++ b/Tests/test_imageops_usm.py @@ -0,0 +1,55 @@ +from tester import * + +from PIL import Image +from PIL import ImageOps +from PIL import ImageFilter + +im = Image.open("Images/lena.ppm") + +def test_ops_api(): + + i = ImageOps.gaussian_blur(im, 2.0) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + # i.save("blur.bmp") + + i = ImageOps.usm(im, 2.0, 125, 8) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + # i.save("usm.bmp") + +def test_filter_api(): + + filter = ImageFilter.GaussianBlur(2.0) + i = im.filter(filter) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + + filter = ImageFilter.UnsharpMask(2.0, 125, 8) + i = im.filter(filter) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + +def test_usm(): + + usm = ImageOps.unsharp_mask + assert_exception(ValueError, lambda: usm(im.convert("1"))) + assert_no_exception(lambda: usm(im.convert("L"))) + assert_exception(ValueError, lambda: usm(im.convert("I"))) + assert_exception(ValueError, lambda: usm(im.convert("F"))) + assert_no_exception(lambda: usm(im.convert("RGB"))) + assert_no_exception(lambda: usm(im.convert("RGBA"))) + assert_no_exception(lambda: usm(im.convert("CMYK"))) + assert_exception(ValueError, lambda: usm(im.convert("YCbCr"))) + +def test_blur(): + + blur = ImageOps.gaussian_blur + assert_exception(ValueError, lambda: blur(im.convert("1"))) + assert_no_exception(lambda: blur(im.convert("L"))) + assert_exception(ValueError, lambda: blur(im.convert("I"))) + assert_exception(ValueError, lambda: blur(im.convert("F"))) + assert_no_exception(lambda: blur(im.convert("RGB"))) + assert_no_exception(lambda: blur(im.convert("RGBA"))) + assert_no_exception(lambda: blur(im.convert("CMYK"))) + assert_exception(ValueError, lambda: blur(im.convert("YCbCr"))) diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py new file mode 100644 index 000000000..573c706b1 --- /dev/null +++ b/Tests/test_imagepalette.py @@ -0,0 +1,44 @@ +from tester import * + +from PIL import Image +from PIL import ImagePalette + +ImagePalette = ImagePalette.ImagePalette + +def test_sanity(): + + assert_no_exception(lambda: ImagePalette("RGB", list(range(256))*3)) + assert_exception(ValueError, lambda: ImagePalette("RGB", list(range(256))*2)) + +def test_getcolor(): + + palette = ImagePalette() + + map = {} + for i in range(256): + map[palette.getcolor((i, i, i))] = i + + assert_equal(len(map), 256) + assert_exception(ValueError, lambda: palette.getcolor((1, 2, 3))) + +def test_file(): + + palette = ImagePalette() + + file = tempfile("temp.lut") + + palette.save(file) + + from PIL.ImagePalette import load, raw + + p = load(file) + + # load returns raw palette information + assert_equal(len(p[0]), 768) + assert_equal(p[1], "RGB") + + p = raw(p[1], p[0]) + assert_true(isinstance(p, ImagePalette)) + + + diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py new file mode 100644 index 000000000..beadff10e --- /dev/null +++ b/Tests/test_imagepath.py @@ -0,0 +1,49 @@ +from tester import * + +from PIL import Image +from PIL import ImagePath + +import array + +def test_path(): + + p = ImagePath.Path(list(range(10))) + + # sequence interface + assert_equal(len(p), 5) + assert_equal(p[0], (0.0, 1.0)) + assert_equal(p[-1], (8.0, 9.0)) + assert_equal(list(p[:1]), [(0.0, 1.0)]) + assert_equal(list(p), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) + + # method sanity check + assert_equal(p.tolist(), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) + assert_equal(p.tolist(1), [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]) + + assert_equal(p.getbbox(), (0.0, 1.0, 8.0, 9.0)) + + assert_equal(p.compact(5), 2) + assert_equal(list(p), [(0.0, 1.0), (4.0, 5.0), (8.0, 9.0)]) + + p.transform((1,0,1,0,1,1)) + assert_equal(list(p), [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)]) + + # alternative constructors + p = ImagePath.Path([0, 1]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([0.0, 1.0]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([0, 1]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([(0, 1)]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p.tolist(0)) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p.tolist(1)) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(array.array("f", [0, 1])) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(array.array("f", [0, 1]).tostring()) + assert_equal(list(p), [(0.0, 1.0)]) diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py new file mode 100644 index 000000000..8d6ac9f3c --- /dev/null +++ b/Tests/test_imageqt.py @@ -0,0 +1,9 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageQt +except ImportError as v: + skip(v) + +success() diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py new file mode 100644 index 000000000..0b244d88c --- /dev/null +++ b/Tests/test_imagesequence.py @@ -0,0 +1,22 @@ +from tester import * + +from PIL import Image +from PIL import ImageSequence + +def test_sanity(): + + file = tempfile("temp.im") + + im = lena("RGB") + im.save(file) + + seq = ImageSequence.Iterator(im) + + index = 0 + for frame in seq: + assert_image_equal(im, frame) + assert_equal(im.tell(), index) + index = index + 1 + + assert_equal(index, 1) + diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py new file mode 100644 index 000000000..99ec005c8 --- /dev/null +++ b/Tests/test_imageshow.py @@ -0,0 +1,6 @@ +from tester import * + +from PIL import Image +from PIL import ImageShow + +success() diff --git a/Tests/test_imagestat.py b/Tests/test_imagestat.py new file mode 100644 index 000000000..02a461e22 --- /dev/null +++ b/Tests/test_imagestat.py @@ -0,0 +1,52 @@ +from tester import * + +from PIL import Image +from PIL import ImageStat + +def test_sanity(): + + im = lena() + + st = ImageStat.Stat(im) + st = ImageStat.Stat(im.histogram()) + st = ImageStat.Stat(im, Image.new("1", im.size, 1)) + + assert_no_exception(lambda: st.extrema) + assert_no_exception(lambda: st.sum) + assert_no_exception(lambda: st.mean) + assert_no_exception(lambda: st.median) + assert_no_exception(lambda: st.rms) + assert_no_exception(lambda: st.sum2) + assert_no_exception(lambda: st.var) + assert_no_exception(lambda: st.stddev) + assert_exception(AttributeError, lambda: st.spam) + + assert_exception(TypeError, lambda: ImageStat.Stat(1)) + +def test_lena(): + + im = lena() + + st = ImageStat.Stat(im) + + # verify a few values + assert_equal(st.extrema[0], (61, 255)) + assert_equal(st.median[0], 197) + assert_equal(st.sum[0], 2954416) + assert_equal(st.sum[1], 2027250) + assert_equal(st.sum[2], 1727331) + +def test_constant(): + + im = Image.new("L", (128, 128), 128) + + st = ImageStat.Stat(im) + + assert_equal(st.extrema[0], (128, 128)) + assert_equal(st.sum[0], 128**3) + assert_equal(st.sum2[0], 128**4) + assert_equal(st.mean[0], 128) + assert_equal(st.median[0], 128) + assert_equal(st.rms[0], 128) + assert_equal(st.var[0], 0) + assert_equal(st.stddev[0], 0) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py new file mode 100644 index 000000000..5c39c9283 --- /dev/null +++ b/Tests/test_imagetk.py @@ -0,0 +1,9 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageTk +except ImportError as v: + skip(v) + +success() diff --git a/Tests/test_imagetransform.py b/Tests/test_imagetransform.py new file mode 100644 index 000000000..884e6bb1c --- /dev/null +++ b/Tests/test_imagetransform.py @@ -0,0 +1,18 @@ +from tester import * + +from PIL import Image +from PIL import ImageTransform + +im = Image.new("L", (100, 100)) + +seq = tuple(range(10)) + +def test_sanity(): + transform = ImageTransform.AffineTransform(seq[:6]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.ExtentTransform(seq[:4]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.QuadTransform(seq[:8]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])]) + assert_no_exception(lambda: im.transform((100, 100), transform)) diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py new file mode 100644 index 000000000..268a75b6f --- /dev/null +++ b/Tests/test_imagewin.py @@ -0,0 +1,6 @@ +from tester import * + +from PIL import Image +from PIL import ImageWin + +success() diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py new file mode 100644 index 000000000..93aa694d8 --- /dev/null +++ b/Tests/test_lib_image.py @@ -0,0 +1,30 @@ +from tester import * + +from PIL import Image + +def test_setmode(): + + im = Image.new("L", (1, 1), 255) + im.im.setmode("1") + assert_equal(im.im.getpixel((0, 0)), 255) + im.im.setmode("L") + assert_equal(im.im.getpixel((0, 0)), 255) + + im = Image.new("1", (1, 1), 1) + im.im.setmode("L") + assert_equal(im.im.getpixel((0, 0)), 255) + im.im.setmode("1") + assert_equal(im.im.getpixel((0, 0)), 255) + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + im.im.setmode("RGB") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) + im.im.setmode("RGBA") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGBX") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGB") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) + + assert_exception(ValueError, lambda: im.im.setmode("L")) + assert_exception(ValueError, lambda: im.im.setmode("RGBABCDE")) diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py new file mode 100644 index 000000000..c295ac652 --- /dev/null +++ b/Tests/test_lib_pack.py @@ -0,0 +1,127 @@ +from tester import * + +from PIL import Image + +def pack(): + pass # not yet + +def test_pack(): + + def pack(mode, rawmode): + if len(mode) == 1: + im = Image.new(mode, (1, 1), 1) + else: + im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)]) + + if py3: + return list(im.tobytes("raw", rawmode)) + else: + return [ord(c) for c in im.tostring("raw", rawmode)] + + assert_equal(pack("1", "1"), [128]) + assert_equal(pack("1", "1;I"), [0]) + assert_equal(pack("1", "1;R"), [1]) + assert_equal(pack("1", "1;IR"), [0]) + + assert_equal(pack("L", "L"), [1]) + + assert_equal(pack("I", "I"), [1, 0, 0, 0]) + + assert_equal(pack("F", "F"), [0, 0, 128, 63]) + + assert_equal(pack("LA", "LA"), [1, 2]) + + assert_equal(pack("RGB", "RGB"), [1, 2, 3]) + assert_equal(pack("RGB", "RGB;L"), [1, 2, 3]) + assert_equal(pack("RGB", "BGR"), [3, 2, 1]) + assert_equal(pack("RGB", "RGBX"), [1, 2, 3, 255]) # 255? + assert_equal(pack("RGB", "BGRX"), [3, 2, 1, 0]) + assert_equal(pack("RGB", "XRGB"), [0, 1, 2, 3]) + assert_equal(pack("RGB", "XBGR"), [0, 3, 2, 1]) + + assert_equal(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255? + + assert_equal(pack("RGBA", "RGBA"), [1, 2, 3, 4]) + + assert_equal(pack("CMYK", "CMYK"), [1, 2, 3, 4]) + assert_equal(pack("YCbCr", "YCbCr"), [1, 2, 3]) + +def test_unpack(): + + def unpack(mode, rawmode, bytes_): + im = None + + if py3: + data = bytes(range(1,bytes_+1)) + im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1) + else: + data = ''.join(chr(i) for i in range(1,bytes_+1)) + im = Image.fromstring(mode, (1, 1), data, "raw", rawmode, 0, 1) + + return im.getpixel((0, 0)) + + def unpack_1(mode, rawmode, value): + assert mode == "1" + im = None + + if py3: + im = Image.frombytes(mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1) + else: + im = Image.fromstring(mode, (8, 1), chr(value), "raw", rawmode, 0, 1) + + return tuple(im.getdata()) + + X = 255 + + assert_equal(unpack_1("1", "1", 1), (0,0,0,0,0,0,0,X)) + assert_equal(unpack_1("1", "1;I", 1), (X,X,X,X,X,X,X,0)) + assert_equal(unpack_1("1", "1;R", 1), (X,0,0,0,0,0,0,0)) + assert_equal(unpack_1("1", "1;IR", 1), (0,X,X,X,X,X,X,X)) + + assert_equal(unpack_1("1", "1", 170), (X,0,X,0,X,0,X,0)) + assert_equal(unpack_1("1", "1;I", 170), (0,X,0,X,0,X,0,X)) + assert_equal(unpack_1("1", "1;R", 170), (0,X,0,X,0,X,0,X)) + assert_equal(unpack_1("1", "1;IR", 170), (X,0,X,0,X,0,X,0)) + + assert_equal(unpack("L", "L;2", 1), 0) + assert_equal(unpack("L", "L;4", 1), 0) + assert_equal(unpack("L", "L", 1), 1) + assert_equal(unpack("L", "L;I", 1), 254) + assert_equal(unpack("L", "L;R", 1), 128) + assert_equal(unpack("L", "L;16", 2), 2) # little endian + assert_equal(unpack("L", "L;16B", 2), 1) # big endian + + assert_equal(unpack("LA", "LA", 2), (1, 2)) + assert_equal(unpack("LA", "LA;L", 2), (1, 2)) + + assert_equal(unpack("RGB", "RGB", 3), (1, 2, 3)) + assert_equal(unpack("RGB", "RGB;L", 3), (1, 2, 3)) + assert_equal(unpack("RGB", "RGB;R", 3), (128, 64, 192)) + assert_equal(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ? + assert_equal(unpack("RGB", "BGR", 3), (3, 2, 1)) + assert_equal(unpack("RGB", "BGR;15", 2), (0, 131, 8)) + assert_equal(unpack("RGB", "BGR;16", 2), (0, 64, 8)) + + assert_equal(unpack("RGB", "RGBX", 4), (1, 2, 3)) + assert_equal(unpack("RGB", "BGRX", 4), (3, 2, 1)) + assert_equal(unpack("RGB", "XRGB", 4), (2, 3, 4)) + assert_equal(unpack("RGB", "XBGR", 4), (4, 3, 2)) + + assert_equal(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4)) + assert_equal(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) + assert_equal(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) + assert_equal(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) + + assert_equal(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? + assert_equal(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) + assert_equal(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) + assert_equal(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255)) + + assert_equal(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4)) + assert_equal(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251)) + + assert_exception(ValueError, lambda: unpack("L", "L", 0)) + assert_exception(ValueError, lambda: unpack("RGB", "RGB", 2)) + assert_exception(ValueError, lambda: unpack("CMYK", "CMYK", 2)) + +run() diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py new file mode 100644 index 000000000..060a68bca --- /dev/null +++ b/Tests/test_mode_i16.py @@ -0,0 +1,106 @@ +from tester import * + +from PIL import Image + +def verify(im1): + im2 = lena("I") + assert_equal(im1.size, im2.size) + pix1 = im1.load() + pix2 = im2.load() + for y in range(im1.size[1]): + for x in range(im1.size[0]): + xy = x, y + if pix1[xy] != pix2[xy]: + failure( + "got %r from mode %s at %s, expected %r" % + (pix1[xy], im1.mode, xy, pix2[xy]) + ) + return + success() + +def test_basic(): + # PIL 1.1 has limited support for 16-bit image data. Check that + # create/copy/transform and save works as expected. + + def basic(mode): + + imIn = lena("I").convert(mode) + verify(imIn) + + w, h = imIn.size + + imOut = imIn.copy() + verify(imOut) # copy + + imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h)) + verify(imOut) # transform + + filename = tempfile("temp.im") + imIn.save(filename) + + imOut = Image.open(filename) + + verify(imIn) + verify(imOut) + + imOut = imIn.crop((0, 0, w, h)) + verify(imOut) + + imOut = Image.new(mode, (w, h), None) + imOut.paste(imIn.crop((0, 0, w//2, h)), (0, 0)) + imOut.paste(imIn.crop((w//2, 0, w, h)), (w//2, 0)) + + verify(imIn) + verify(imOut) + + imIn = Image.new(mode, (1, 1), 1) + assert_equal(imIn.getpixel((0, 0)), 1) + + imIn.putpixel((0, 0), 2) + assert_equal(imIn.getpixel((0, 0)), 2) + + if mode == "L": + max = 255 + else: + max = 32767 + + imIn = Image.new(mode, (1, 1), 256) + assert_equal(imIn.getpixel((0, 0)), min(256, max)) + + imIn.putpixel((0, 0), 512) + assert_equal(imIn.getpixel((0, 0)), min(512, max)) + + basic("L") + + basic("I;16") + basic("I;16B") + basic("I;16L") + + basic("I") + + +def test_tostring(): + + def tostring(mode): + if py3: + return Image.new(mode, (1, 1), 1).tobytes() + else: + return Image.new(mode, (1, 1), 1).tostring() + + assert_equal(tostring("L"), b"\x01") + assert_equal(tostring("I;16"), b"\x01\x00") + assert_equal(tostring("I;16B"), b"\x00\x01") + assert_equal(tostring("I"), b"\x01\x00\x00\x00") + + +def test_convert(): + + im = lena("I") + + verify(im.convert("I;16")) + verify(im.convert("I;16").convert("L")) + verify(im.convert("I;16").convert("I")) + + verify(im.convert("I;16B")) + verify(im.convert("I;16B").convert("L")) + verify(im.convert("I;16B").convert("I")) diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py new file mode 100644 index 000000000..ea154afb8 --- /dev/null +++ b/Tests/test_numpy.py @@ -0,0 +1,54 @@ +from tester import * + +from PIL import Image + +try: + import site + import numpy +except ImportError: + skip() + +def test_numpy_to_image(): + + def to_image(dtype, bands=1, bool=0): + if bands == 1: + if bool: + data = [0, 1] * 50 + else: + data = list(range(100)) + a = numpy.array(data, dtype=dtype) + a.shape = 10, 10 + i = Image.fromarray(a) + if list(i.getdata()) != data: + print("data mismatch for", dtype) + else: + data = list(range(100)) + a = numpy.array([[x]*bands for x in data], dtype=dtype) + a.shape = 10, 10, bands + i = Image.fromarray(a) + if list(i.split()[0].getdata()) != list(range(100)): + print("data mismatch for", dtype) + # print dtype, list(i.getdata()) + return i + + # assert_image(to_image(numpy.bool, bool=1), "1", (10, 10)) + # assert_image(to_image(numpy.bool8, bool=1), "1", (10, 10)) + + assert_exception(TypeError, lambda: to_image(numpy.uint)) + assert_image(to_image(numpy.uint8), "L", (10, 10)) + assert_exception(TypeError, lambda: to_image(numpy.uint16)) + assert_exception(TypeError, lambda: to_image(numpy.uint32)) + assert_exception(TypeError, lambda: to_image(numpy.uint64)) + + assert_image(to_image(numpy.int), "I", (10, 10)) + assert_image(to_image(numpy.int8), "I", (10, 10)) + assert_image(to_image(numpy.int16), "I;16", (10, 10)) + assert_image(to_image(numpy.int32), "I", (10, 10)) + assert_exception(TypeError, lambda: to_image(numpy.int64)) + + assert_image(to_image(numpy.float), "F", (10, 10)) + assert_image(to_image(numpy.float32), "F", (10, 10)) + assert_image(to_image(numpy.float64), "F", (10, 10)) + + assert_image(to_image(numpy.uint8, 3), "RGB", (10, 10)) + assert_image(to_image(numpy.uint8, 4), "RGBA", (10, 10)) diff --git a/Tests/tester.py b/Tests/tester.py new file mode 100644 index 000000000..14e4a2d2d --- /dev/null +++ b/Tests/tester.py @@ -0,0 +1,237 @@ +from __future__ import print_function + +import sys +py3 = (sys.version_info >= (3,0)) + +# some test helpers + +_target = None +_tempfiles = [] +_logfile = None + +def success(): + import sys + success.count += 1 + if _logfile: + print(sys.argv[0], success.count, failure.count, file=_logfile) + +def failure(msg=None, frame=None): + import sys, linecache + failure.count += 1 + if _target: + if frame is None: + frame = sys._getframe() + while frame.f_globals.get("__name__") != _target.__name__: + frame = frame.f_back + location = (frame.f_code.co_filename, frame.f_lineno) + prefix = "%s:%d: " % location + line = linecache.getline(*location) + print(prefix + line.strip() + " failed:") + if msg: + print("- " + msg) + if _logfile: + print(sys.argv[0], success.count, failure.count, file=_logfile) + +success.count = failure.count = 0 + +# predicates + +def assert_true(v, msg=None): + if v: + success() + else: + failure(msg or "got %r, expected true value" % v) + +def assert_false(v, msg=None): + if v: + failure(msg or "got %r, expected false value" % v) + else: + success() + +def assert_equal(a, b, msg=None): + if a == b: + success() + else: + failure(msg or "got %r, expected %r" % (a, b)) + +def assert_match(v, pattern, msg=None): + import re + if re.match(pattern, v): + success() + else: + failure(msg or "got %r, doesn't match pattern %r" % (v, pattern)) + +def assert_exception(exc_class, func): + import sys, traceback + try: + func() + except exc_class: + success() + except: + failure("expected %r exception, got %r" % ( + exc_class.__name__, sys.exc_info()[0].__name__)) + traceback.print_exc() + else: + failure("expected %r exception, got no exception" % exc_class.__name__) + +def assert_no_exception(func): + import sys, traceback + try: + func() + except: + failure("expected no exception, got %r" % sys.exc_info()[0].__name__) + traceback.print_exc() + else: + success() + +def assert_warning(warn_class, func): + # note: this assert calls func three times! + import warnings + def warn_error(message, category, **options): + raise category(message) + def warn_ignore(message, category, **options): + pass + warn = warnings.warn + result = None + try: + warnings.warn = warn_ignore + assert_no_exception(func) + result = func() + warnings.warn = warn_error + assert_exception(warn_class, func) + finally: + warnings.warn = warn # restore + return result + +# helpers + +from io import BytesIO + +def fromstring(data): + from PIL import Image + return Image.open(BytesIO(data)) + +def tostring(im, format, **options): + out = BytesIO() + im.save(out, format, **options) + return out.getvalue() + +def lena(mode="RGB", cache={}): + from PIL import Image + im = cache.get(mode) + if im is None: + if mode == "RGB": + im = Image.open("Images/lena.ppm") + elif mode == "F": + im = lena("L").convert(mode) + elif mode[:4] == "I;16": + im = lena("I").convert(mode) + else: + im = lena("RGB").convert(mode) + cache[mode] = im + return im + +def assert_image(im, mode, size, msg=None): + if mode is not None and im.mode != mode: + failure(msg or "got mode %r, expected %r" % (im.mode, mode)) + elif size is not None and im.size != size: + failure(msg or "got size %r, expected %r" % (im.size, size)) + else: + success() + +def assert_image_equal(a, b, msg=None): + if a.mode != b.mode: + failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) + elif a.size != b.size: + failure(msg or "got size %r, expected %r" % (a.size, b.size)) + elif py3 and a.tobytes() != b.tobytes(): + failure(msg or "got different content") + # generate better diff? + elif not py3 and a.tostring() != b.tostring(): + failure(msg or "got different content") + # same complaint? + else: + success() + +def tempfile(template, *extra): + import os, sys + files = [] + for temp in (template,) + extra: + assert temp[:5] in ("temp.", "temp_") + root, name = os.path.split(sys.argv[0]) + name = temp[:4] + os.path.splitext(name)[0][4:] + name = name + "_%d" % len(_tempfiles) + temp[4:] + name = os.path.join(root, name) + files.append(name) + _tempfiles.extend(files) + return files[0] + +# test runner + +def run(): + global _target, _tests, run + import sys, traceback + _target = sys.modules["__main__"] + run = None # no need to run twice + tests = [] + for name, value in list(vars(_target).items()): + if name[:5] == "test_" and type(value) is type(success): + tests.append((value.__code__.co_firstlineno, name, value)) + tests.sort() # sort by line + for lineno, name, func in tests: + try: + _tests = [] + func() + for func, args in _tests: + func(*args) + except: + t, v, tb = sys.exc_info() + tb = tb.tb_next + if tb: + failure(frame=tb.tb_frame) + traceback.print_exception(t, v, tb) + else: + print("%s:%d: cannot call test function: %s" % ( + sys.argv[0], lineno, v)) + failure.count += 1 + +def yield_test(function, *args): + # collect delayed/generated tests + _tests.append((function, args)) + +def skip(msg=None): + import os + print("skip") + os._exit(0) # don't run exit handlers + +def _setup(): + global _logfile + def report(): + if run: + run() + if success.count and not failure.count: + print("ok") + # only clean out tempfiles if test passed + import os + for file in _tempfiles: + try: + os.remove(file) + except OSError: + pass # report? + if "--coverage" in sys.argv: + import coverage + coverage.stop() + # The coverage module messes up when used from inside an + # atexit handler. Do an explicit save to make sure that + # we actually flush the coverage cache. + coverage.the_coverage.save() + import atexit, sys + atexit.register(report) + if "--coverage" in sys.argv: + import coverage + coverage.start() + if "--log" in sys.argv: + _logfile = open("test.log", "a") + + +_setup() diff --git a/Tests/threaded_save.py b/Tests/threaded_save.py new file mode 100644 index 000000000..8162e713c --- /dev/null +++ b/Tests/threaded_save.py @@ -0,0 +1,56 @@ +from PIL import Image + +import sys, time +import io +import threading, queue + +try: + format = sys.argv[1] +except: + format = "PNG" + +im = Image.open("Images/lena.ppm") +im.load() + +queue = queue.Queue() + +result = [] + +class Worker(threading.Thread): + def run(self): + while 1: + im = queue.get() + if im is None: + queue.task_done() + sys.stdout.write("x") + break + f = io.BytesIO() + im.save(f, format, optimize=1) + data = f.getvalue() + result.append(len(data)) + im = Image.open(io.BytesIO(data)) + im.load() + sys.stdout.write(".") + queue.task_done() + +t0 = time.time() + +threads = 20 +jobs = 100 + +for i in range(threads): + w = Worker() + w.start() + +for i in range(jobs): + queue.put(im) + +for i in range(threads): + queue.put(None) + +queue.join() + +print() +print(time.time() - t0) +print(len(result), sum(result)) +print(result) diff --git a/Tests/versions.py b/Tests/versions.py new file mode 100644 index 000000000..a4e4a0bc2 --- /dev/null +++ b/Tests/versions.py @@ -0,0 +1,23 @@ +from PIL import Image + +def version(module, version): + v = getattr(module.core, version + "_version", None) + if v: + print(version, v) + +version(Image, "jpeglib") +version(Image, "zlib") + +try: + from PIL import ImageFont +except ImportError: + pass +else: + version(ImageFont, "freetype2") + +try: + from PIL import ImageCms +except ImportError: + pass +else: + version(ImageCms, "littlecms") diff --git a/_imaging.c b/_imaging.c index 9afa08812..986f221e4 100644 --- a/_imaging.c +++ b/_imaging.c @@ -76,6 +76,7 @@ #include "Imaging.h" +#include "py3.h" /* Configuration stuff. Feel free to undef things you don't need. */ #define WITH_IMAGECHOPS /* ImageChops support */ @@ -103,19 +104,6 @@ #define L16(p, i) ((((int)p[(i)+1]) << 8) + p[(i)]) #define S16(v) ((v) < 32768 ? (v) : ((v) - 65536)) -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - -#if PY_VERSION_HEX < 0x02050000 -#define Py_ssize_t int -#define ssizeargfunc intargfunc -#define ssizessizeargfunc intintargfunc -#define ssizeobjargproc intobjargproc -#define ssizessizeobjargproc intintobjargproc -#endif - /* -------------------------------------------------------------------- */ /* OBJECT ADMINISTRATION */ /* -------------------------------------------------------------------- */ @@ -126,7 +114,7 @@ typedef struct { ImagingAccess access; } ImagingObject; -staticforward PyTypeObject Imaging_Type; +static PyTypeObject Imaging_Type; #ifdef WITH_IMAGEDRAW @@ -148,7 +136,7 @@ typedef struct { Glyph glyphs[256]; } ImagingFontObject; -staticforward PyTypeObject ImagingFont_Type; +static PyTypeObject ImagingFont_Type; typedef struct { PyObject_HEAD @@ -157,7 +145,7 @@ typedef struct { int blend; } ImagingDrawObject; -staticforward PyTypeObject ImagingDraw_Type; +static PyTypeObject ImagingDraw_Type; #endif @@ -167,7 +155,7 @@ typedef struct { int readonly; } PixelAccessObject; -staticforward PyTypeObject PixelAccess_Type; +static PyTypeObject PixelAccess_Type; PyObject* PyImagingNew(Imaging imOut) @@ -207,7 +195,7 @@ _dealloc(ImagingObject* imagep) PyObject_Del(imagep); } -#define PyImaging_Check(op) ((op)->ob_type == &Imaging_Type) +#define PyImaging_Check(op) (Py_TYPE(op) == &Imaging_Type) Imaging PyImaging_AsImaging(PyObject *op) { @@ -243,43 +231,38 @@ void ImagingSectionLeave(ImagingSectionCookie* cookie) /* -------------------------------------------------------------------- */ /* Python compatibility API */ -#if PY_VERSION_HEX < 0x02020000 - -int PyImaging_CheckBuffer(PyObject *buffer) -{ - PyBufferProcs *procs = buffer->ob_type->tp_as_buffer; - if (procs && procs->bf_getreadbuffer && procs->bf_getsegcount && - procs->bf_getsegcount(buffer, NULL) == 1) - return 1; - return 0; -} - -int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr) -{ - PyBufferProcs *procs = buffer->ob_type->tp_as_buffer; - return procs->bf_getreadbuffer(buffer, 0, ptr); -} - -#else - int PyImaging_CheckBuffer(PyObject* buffer) { - return PyObject_CheckReadBuffer(buffer); +#if PY_VERSION_HEX >= 0x03000000 + return PyObject_CheckBuffer(buffer); +#else + return PyObject_CheckBuffer(buffer) || PyObject_CheckReadBuffer(buffer); +#endif } -int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr) +int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view) { /* must call check_buffer first! */ -#if PY_VERSION_HEX < 0x02050000 - int n = 0; +#if PY_VERSION_HEX >= 0x03000000 + return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE); #else - Py_ssize_t n = 0; -#endif - PyObject_AsReadBuffer(buffer, ptr, &n); - return (int) n; -} + /* Use new buffer protocol if available + (mmap doesn't support this in 2.7, go figure) */ + if (PyObject_CheckBuffer(buffer)) { + return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE); + } + /* Pretend we support the new protocol; PyBuffer_Release happily ignores + calling bf_releasebuffer on objects that don't support it */ + *view = (Py_buffer) {0}; + view->readonly = 1; + + Py_INCREF(buffer); + view->obj = buffer; + + return PyObject_AsReadBuffer(buffer, (void *) &view->buf, &view->len); #endif +} /* -------------------------------------------------------------------- */ /* EXCEPTION REROUTING */ @@ -527,8 +510,17 @@ getink(PyObject* color, Imaging im, char* ink) ink[1] = ink[2] = ink[3] = 0; } else { a = 255; - if (PyInt_Check(color)) { - r = PyInt_AS_LONG(color); +#if PY_VERSION_HEX >= 0x03000000 + if (PyLong_Check(color)) { + r = (int) PyLong_AsLong(color); +#else + if (PyInt_Check(color) || PyLong_Check(color)) { + if (PyInt_Check(color)) + r = PyInt_AS_LONG(color); + else + r = (int) PyLong_AsLong(color); +#endif + /* compatibility: ABGR */ a = (UINT8) (r >> 24); b = (UINT8) (r >> 16); @@ -914,11 +906,11 @@ _getpalette(ImagingObject* self, PyObject* args) return NULL; } - palette = PyString_FromStringAndSize(NULL, palettesize * bits / 8); + palette = PyBytes_FromStringAndSize(NULL, palettesize * bits / 8); if (!palette) return NULL; - pack((UINT8*) PyString_AsString(palette), + pack((UINT8*) PyBytes_AsString(palette), self->image->palette->palette, palettesize); return palette; @@ -1222,8 +1214,9 @@ _putdata(ImagingObject* self, PyObject* args) PyObject* data; double scale = 1.0; double offset = 0.0; + if (!PyArg_ParseTuple(args, "O|dd", &data, &scale, &offset)) - return NULL; + return NULL; if (!PySequence_Check(data)) { PyErr_SetString(PyExc_TypeError, must_be_sequence); @@ -1239,9 +1232,9 @@ _putdata(ImagingObject* self, PyObject* args) } if (image->image8) { - if (PyString_Check(data)) { + if (PyBytes_Check(data)) { unsigned char* p; - p = (unsigned char*) PyString_AS_STRING((PyStringObject*) data); + p = (unsigned char*) PyBytes_AS_STRING(data); if (scale == 1.0 && offset == 0.0) /* Plain string data */ for (i = y = 0; i < n; i += image->xsize, y++) { @@ -1326,14 +1319,18 @@ _putdata(ImagingObject* self, PyObject* args) break; default: for (i = x = y = 0; i < n; i++) { - char ink[4]; + union { + char ink[4]; + INT32 inkint; + } u; + PyObject *op = PySequence_GetItem(data, i); - if (!op || !getink(op, image, ink)) { + if (!op || !getink(op, image, u.ink)) { Py_DECREF(op); return NULL; } /* FIXME: what about scale and offset? */ - image->image32[y][x] = *((INT32*) ink); + image->image32[y][x] = u.inkint; Py_XDECREF(op); if (++x >= (int) image->xsize) x = 0, y++; @@ -1379,7 +1376,7 @@ _putpalette(ImagingObject* self, PyObject* args) char* rawmode; UINT8* palette; int palettesize; - if (!PyArg_ParseTuple(args, "ss#", &rawmode, &palette, &palettesize)) + if (!PyArg_ParseTuple(args, "s"PY_ARG_BYTES_LENGTH, &rawmode, &palette, &palettesize)) return NULL; if (strcmp(self->image->mode, "L") != 0 && strcmp(self->image->mode, "P")) { @@ -1874,8 +1871,9 @@ _getprojection(ImagingObject* self, PyObject* args) ImagingGetProjection(self->image, (unsigned char *)xprofile, (unsigned char *)yprofile); - result = Py_BuildValue("s#s#", xprofile, self->image->xsize, - yprofile, self->image->ysize); + result = Py_BuildValue(PY_ARG_BYTES_LENGTH PY_ARG_BYTES_LENGTH, + xprofile, self->image->xsize, + yprofile, self->image->ysize); free(xprofile); free(yprofile); @@ -2102,7 +2100,7 @@ _font_new(PyObject* self_, PyObject* args) ImagingObject* imagep; unsigned char* glyphdata; int glyphdata_length; - if (!PyArg_ParseTuple(args, "O!s#", + if (!PyArg_ParseTuple(args, "O!"PY_ARG_BYTES_LENGTH, &Imaging_Type, &imagep, &glyphdata, &glyphdata_length)) return NULL; @@ -2232,12 +2230,6 @@ static struct PyMethodDef _font_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_font_getattr(ImagingFontObject* self, char* name) -{ - return Py_FindMethod(_font_methods, (PyObject*) self, name); -} - /* -------------------------------------------------------------------- */ static PyObject* @@ -2670,12 +2662,6 @@ static struct PyMethodDef _draw_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_draw_getattr(ImagingDrawObject* self, char* name) -{ - return Py_FindMethod(_draw_methods, (PyObject*) self, name); -} - #endif @@ -2812,7 +2798,8 @@ _crc32(PyObject* self, PyObject* args) hi = lo = 0; - if (!PyArg_ParseTuple(args, "s#|(ii)", &buffer, &bytes, &hi, &lo)) + if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"|(ii)", + &buffer, &bytes, &hi, &lo)) return NULL; crc = ((UINT32) (hi & 0xFFFF) << 16) + (lo & 0xFFFF); @@ -2843,11 +2830,10 @@ _getcodecstatus(PyObject* self, PyObject* args) case IMAGING_CODEC_MEMORY: msg = "out of memory"; break; default: - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } - return PyString_FromString(msg); + return PyUnicode_FromString(msg); } /* -------------------------------------------------------------------- */ @@ -2978,29 +2964,48 @@ static struct PyMethodDef methods[] = { /* attributes */ -static PyObject* -_getattr(ImagingObject* self, char* name) +static PyObject* +_getattr_mode(ImagingObject* self, void* closure) { - PyObject* res; - - res = Py_FindMethod(methods, (PyObject*) self, name); - if (res) - return res; - PyErr_Clear(); - if (strcmp(name, "mode") == 0) - return PyString_FromString(self->image->mode); - if (strcmp(name, "size") == 0) - return Py_BuildValue("ii", self->image->xsize, self->image->ysize); - if (strcmp(name, "bands") == 0) - return PyInt_FromLong(self->image->bands); - if (strcmp(name, "id") == 0) - return PyInt_FromLong((long) self->image); - if (strcmp(name, "ptr") == 0) - return PyCObject_FromVoidPtrAndDesc(self->image, IMAGING_MAGIC, NULL); - PyErr_SetString(PyExc_AttributeError, name); - return NULL; + return PyUnicode_FromString(self->image->mode); } +static PyObject* +_getattr_size(ImagingObject* self, void* closure) +{ + return Py_BuildValue("ii", self->image->xsize, self->image->ysize); +} + +static PyObject* +_getattr_bands(ImagingObject* self, void* closure) +{ + return PyInt_FromLong(self->image->bands); +} + +static PyObject* +_getattr_id(ImagingObject* self, void* closure) +{ + return PyInt_FromLong((long) self->image); +} + +static PyObject* +_getattr_ptr(ImagingObject* self, void* closure) +{ +#if PY_VERSION_HEX >= 0x03020000 + return PyCapsule_New(self->image, IMAGING_MAGIC, NULL); +#else + return PyCObject_FromVoidPtrAndDesc(self->image, IMAGING_MAGIC, NULL); +#endif +} + +static struct PyGetSetDef getsetters[] = { + { "mode", (getter) _getattr_mode }, + { "size", (getter) _getattr_size }, + { "bands", (getter) _getattr_bands }, + { "id", (getter) _getattr_id }, + { "ptr", (getter) _getattr_ptr }, + { NULL } +}; /* basic sequence semantics */ @@ -3028,7 +3033,7 @@ image_item(ImagingObject *self, Py_ssize_t i) } static PySequenceMethods image_as_sequence = { - (inquiry) image_length, /*sq_length*/ + (lenfunc) image_length, /*sq_length*/ (binaryfunc) NULL, /*sq_concat*/ (ssizeargfunc) NULL, /*sq_repeat*/ (ssizeargfunc) image_item, /*sq_item*/ @@ -3040,64 +3045,123 @@ static PySequenceMethods image_as_sequence = { /* type description */ -statichere PyTypeObject Imaging_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject Imaging_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingCore", /*tp_name*/ sizeof(ImagingObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ 0, /*tp_as_number */ &image_as_sequence, /*tp_as_sequence */ 0, /*tp_as_mapping */ - 0 /*tp_hash*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + getsetters, /*tp_getset*/ }; #ifdef WITH_IMAGEDRAW -statichere PyTypeObject ImagingFont_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject ImagingFont_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingFont", /*tp_name*/ sizeof(ImagingFontObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_font_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_font_getattr, /*tp_getattr*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _font_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; -statichere PyTypeObject ImagingDraw_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject ImagingDraw_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingDraw", /*tp_name*/ sizeof(ImagingDrawObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_draw_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_draw_getattr, /*tp_getattr*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _draw_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; #endif static PyMappingMethods pixel_access_as_mapping = { - (inquiry) NULL, /*mp_length*/ + (lenfunc) NULL, /*mp_length*/ (binaryfunc) pixel_access_getitem, /*mp_subscript*/ (objobjargproc) pixel_access_setitem, /*mp_ass_subscript*/ }; /* type description */ -statichere PyTypeObject PixelAccess_Type = { - PyObject_HEAD_INIT(NULL) - 0, "PixelAccess", sizeof(PixelAccessObject), 0, +static PyTypeObject PixelAccess_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "PixelAccess", sizeof(PixelAccessObject), 0, /* methods */ (destructor)pixel_access_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ @@ -3261,36 +3325,69 @@ static PyMethodDef functions[] = { {NULL, NULL} /* sentinel */ }; -DL_EXPORT(void) -init_imaging(void) -{ - PyObject* m; - PyObject* d; +static int +setup_module(PyObject* m) { + PyObject* d = PyModule_GetDict(m); + + /* Ready object types */ + if (PyType_Ready(&Imaging_Type) < 0) + return -1; - /* Patch object type */ - Imaging_Type.ob_type = &PyType_Type; #ifdef WITH_IMAGEDRAW - ImagingFont_Type.ob_type = &PyType_Type; - ImagingDraw_Type.ob_type = &PyType_Type; + if (PyType_Ready(&ImagingFont_Type) < 0) + return -1; + + if (PyType_Ready(&ImagingDraw_Type) < 0) + return -1; #endif - PixelAccess_Type.ob_type = &PyType_Type; + if (PyType_Ready(&PixelAccess_Type) < 0) + return -1; ImagingAccessInit(); - m = Py_InitModule("_imaging", functions); - d = PyModule_GetDict(m); - #ifdef HAVE_LIBJPEG { extern const char* ImagingJpegVersion(void); - PyDict_SetItemString(d, "jpeglib_version", PyString_FromString(ImagingJpegVersion())); + PyDict_SetItemString(d, "jpeglib_version", PyUnicode_FromString(ImagingJpegVersion())); } #endif #ifdef HAVE_LIBZ { extern const char* ImagingZipVersion(void); - PyDict_SetItemString(d, "zlib_version", PyString_FromString(ImagingZipVersion())); + PyDict_SetItemString(d, "zlib_version", PyUnicode_FromString(ImagingZipVersion())); } #endif + + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imaging(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imaging", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + functions, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imaging(void) +{ + PyObject* m = Py_InitModule("_imaging", functions); + setup_module(m); +} +#endif + diff --git a/_imagingcms.c b/_imagingcms.c index a53351f42..6b215cabb 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -26,11 +26,7 @@ http://www.cazabon.com\n\ #include "Python.h" #include "lcms.h" #include "Imaging.h" - -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif +#include "py3.h" #if LCMS_VERSION < 117 #define LCMSBOOL BOOL @@ -83,9 +79,9 @@ typedef struct { cmsHPROFILE profile; } CmsProfileObject; -staticforward PyTypeObject CmsProfile_Type; +static PyTypeObject CmsProfile_Type; -#define CmsProfile_Check(op) ((op)->ob_type == &CmsProfile_Type) +#define CmsProfile_Check(op) (Py_TYPE(op) == &CmsProfile_Type) static PyObject* cms_profile_new(cmsHPROFILE profile) @@ -128,8 +124,13 @@ cms_profile_fromstring(PyObject* self, PyObject* args) char* pProfile; int nProfile; +#if PY_VERSION_HEX >= 0x03000000 + if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile)) + return NULL; +#else if (!PyArg_ParseTuple(args, "s#:profile_fromstring", &pProfile, &nProfile)) return NULL; +#endif cmsErrorAction(LCMS_ERROR_IGNORE); @@ -158,9 +159,9 @@ typedef struct { cmsHTRANSFORM transform; } CmsTransformObject; -staticforward PyTypeObject CmsTransform_Type; +static PyTypeObject CmsTransform_Type; -#define CmsTransform_Check(op) ((op)->ob_type == &CmsTransform_Type) +#define CmsTransform_Check(op) (Py_TYPE(op) == &CmsTransform_Type) static PyObject* cms_transform_new(cmsHTRANSFORM transform, char* mode_in, char* mode_out) @@ -495,7 +496,10 @@ cms_get_display_profile_win32(PyObject* self, PyObject* args) static PyMethodDef pyCMSdll_methods[] = { {"profile_open", cms_profile_open, 1}, + {"profile_frombytes", cms_profile_fromstring, 1}, +#if PY_VERSION_HEX < 0x03000000 {"profile_fromstring", cms_profile_fromstring, 1}, +#endif /* profile and transform functions */ {"buildTransform", buildTransform, 1}, @@ -515,40 +519,83 @@ static struct PyMethodDef cms_profile_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -cms_profile_getattr(CmsProfileObject* self, char* name) +static PyObject* +cms_profile_getattr_product_name(CmsProfileObject* self, void* closure) { - if (!strcmp(name, "product_name")) - return PyString_FromString(cmsTakeProductName(self->profile)); - if (!strcmp(name, "product_desc")) - return PyString_FromString(cmsTakeProductDesc(self->profile)); - if (!strcmp(name, "product_info")) - return PyString_FromString(cmsTakeProductInfo(self->profile)); - if (!strcmp(name, "rendering_intent")) - return PyInt_FromLong(cmsTakeRenderingIntent(self->profile)); - if (!strcmp(name, "pcs")) - return PyString_FromString(findICmode(cmsGetPCS(self->profile))); - if (!strcmp(name, "color_space")) - return PyString_FromString(findICmode(cmsGetColorSpace(self->profile))); - /* FIXME: add more properties (creation_datetime etc) */ - - return Py_FindMethod(cms_profile_methods, (PyObject*) self, name); + return PyUnicode_FromString(cmsTakeProductName(self->profile)); } -statichere PyTypeObject CmsProfile_Type = { - PyObject_HEAD_INIT(NULL) - 0, "CmsProfile", sizeof(CmsProfileObject), 0, +static PyObject* +cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) +{ + return PyUnicode_FromString(cmsTakeProductDesc(self->profile)); +} + +static PyObject* +cms_profile_getattr_product_info(CmsProfileObject* self, void* closure) +{ + return PyUnicode_FromString(cmsTakeProductInfo(self->profile)); +} + +static PyObject* +cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure) +{ + return PyInt_FromLong(cmsTakeRenderingIntent(self->profile)); +} + +static PyObject* +cms_profile_getattr_pcs(CmsProfileObject* self, void* closure) +{ + return PyUnicode_FromString(findICmode(cmsGetPCS(self->profile))); +} + +static PyObject* +cms_profile_getattr_color_space(CmsProfileObject* self, void* closure) +{ + return PyUnicode_FromString(findICmode(cmsGetColorSpace(self->profile))); +} + +/* FIXME: add more properties (creation_datetime etc) */ +static struct PyGetSetDef cms_profile_getsetters[] = { + { "product_name", (getter) cms_profile_getattr_product_name }, + { "product_desc", (getter) cms_profile_getattr_product_desc }, + { "product_info", (getter) cms_profile_getattr_product_info }, + { "rendering_intent", (getter) cms_profile_getattr_rendering_intent }, + { "pcs", (getter) cms_profile_getattr_pcs }, + { "color_space", (getter) cms_profile_getattr_color_space }, + { NULL } +}; + +static PyTypeObject CmsProfile_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "CmsProfile", sizeof(CmsProfileObject), 0, /* methods */ (destructor) cms_profile_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc) cms_profile_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number */ - 0, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0 /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + cms_profile_methods, /*tp_methods*/ + 0, /*tp_members*/ + cms_profile_getsetters, /*tp_getset*/ }; static struct PyMethodDef cms_transform_methods[] = { @@ -556,55 +603,101 @@ static struct PyMethodDef cms_transform_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -cms_transform_getattr(CmsTransformObject* self, char* name) +static PyObject* +cms_transform_getattr_inputMode(CmsTransformObject* self, void* closure) { - if (!strcmp(name, "inputMode")) - return PyString_FromString(self->mode_in); - if (!strcmp(name, "outputMode")) - return PyString_FromString(self->mode_out); - - return Py_FindMethod(cms_transform_methods, (PyObject*) self, name); + return PyUnicode_FromString(self->mode_in); } -statichere PyTypeObject CmsTransform_Type = { - PyObject_HEAD_INIT(NULL) - 0, "CmsTransform", sizeof(CmsTransformObject), 0, +static PyObject* +cms_transform_getattr_outputMode(CmsTransformObject* self, void* closure) +{ + return PyUnicode_FromString(self->mode_out); +} + +static struct PyGetSetDef cms_transform_getsetters[] = { + { "inputMode", (getter) cms_transform_getattr_inputMode }, + { "outputMode", (getter) cms_transform_getattr_outputMode }, + { NULL } +}; + +static PyTypeObject CmsTransform_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "CmsTransform", sizeof(CmsTransformObject), 0, /* methods */ (destructor) cms_transform_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc) cms_transform_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number */ - 0, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0 /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + cms_transform_methods, /*tp_methods*/ + 0, /*tp_members*/ + cms_transform_getsetters, /*tp_getset*/ }; -DL_EXPORT(void) -init_imagingcms(void) -{ - PyObject *m; +static int +setup_module(PyObject* m) { PyObject *d; PyObject *v; - /* Patch up object types */ - CmsProfile_Type.ob_type = &PyType_Type; - CmsTransform_Type.ob_type = &PyType_Type; - - m = Py_InitModule("_imagingcms", pyCMSdll_methods); d = PyModule_GetDict(m); -#if PY_VERSION_HEX >= 0x02020000 - v = PyString_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); -#else - { - char buffer[100]; - sprintf(buffer, "%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); - v = PyString_FromString(buffer); - } -#endif + /* Ready object types */ + PyType_Ready(&CmsProfile_Type); + PyType_Ready(&CmsTransform_Type); + + d = PyModule_GetDict(m); + + v = PyUnicode_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); PyDict_SetItemString(d, "littlecms_version", v); + + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingcms(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingcms", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + pyCMSdll_methods, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imagingcms(void) +{ + PyObject *m = Py_InitModule("_imagingcms", pyCMSdll_methods); + setup_module(m); +} +#endif + diff --git a/_imagingft.c b/_imagingft.c index 935808718..f54e38553 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -35,21 +35,8 @@ #include #endif -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - -#if PY_VERSION_HEX >= 0x01060000 -#if PY_VERSION_HEX < 0x02020000 || defined(Py_USING_UNICODE) -/* defining this enables unicode support (default under 1.6a1 and later) */ -#define HAVE_UNICODE -#endif -#endif - -#if !defined(Py_RETURN_NONE) -#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None -#endif +#define KEEP_PY_UNICODE +#include "py3.h" #if !defined(FT_LOAD_TARGET_MONO) #define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME @@ -82,7 +69,7 @@ typedef struct { FT_Face face; } FontObject; -staticforward PyTypeObject Font_Type; +static PyTypeObject Font_Type; /* round a 26.6 pixel coordinate to the nearest larger integer */ #define PIXEL(x) ((((x)+63) & -64)>>6) @@ -118,16 +105,10 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) "filename", "size", "index", "encoding", NULL }; -#if defined(HAVE_UNICODE) && PY_VERSION_HEX >= 0x02020000 if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|is", kwlist, Py_FileSystemDefaultEncoding, &filename, &size, &index, &encoding)) return NULL; -#else - if (!PyArg_ParseTupleAndKeywords(args, kw, "si|is", kwlist, - &filename, &size, &index, &encoding)) - return NULL; -#endif if (!library) { PyErr_SetString( @@ -164,7 +145,6 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) static int font_getchar(PyObject* string, int index, FT_ULong* char_out) { -#if defined(HAVE_UNICODE) if (PyUnicode_Check(string)) { Py_UNICODE* p = PyUnicode_AS_UNICODE(string); int size = PyUnicode_GET_SIZE(string); @@ -173,7 +153,8 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out) *char_out = p[index]; return 1; } -#endif + +#if PY_VERSION_HEX < 0x03000000 if (PyString_Check(string)) { unsigned char* p = (unsigned char*) PyString_AS_STRING(string); int size = PyString_GET_SIZE(string); @@ -182,6 +163,8 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out) *char_out = (unsigned char) p[index]; return 1; } +#endif + return 0; } @@ -201,10 +184,10 @@ font_getsize(FontObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "O:getsize", &string)) return NULL; -#if defined(HAVE_UNICODE) - if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_Check(string)) { #else - if (!PyString_Check(string)) { + if (!PyUnicode_Check(string) && !PyString_Check(string)) { #endif PyErr_SetString(PyExc_TypeError, "expected string"); return NULL; @@ -267,10 +250,10 @@ font_getabc(FontObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "O:getabc", &string)) return NULL; -#if defined(HAVE_UNICODE) - if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_Check(string)) { #else - if (!PyString_Check(string)) { + if (!PyUnicode_Check(string) && !PyString_Check(string)) { #endif PyErr_SetString(PyExc_TypeError, "expected string"); return NULL; @@ -315,10 +298,10 @@ font_render(FontObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "Ol|i:render", &string, &id, &mask)) return NULL; -#if defined(HAVE_UNICODE) - if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_Check(string)) { #else - if (!PyString_Check(string)) { + if (!PyUnicode_Check(string) && !PyString_Check(string)) { #endif PyErr_SetString(PyExc_TypeError, "expected string"); return NULL; @@ -420,49 +403,89 @@ static PyMethodDef font_methods[] = { {NULL, NULL} }; -static PyObject* -font_getattr(FontObject* self, char* name) +static PyObject* +font_getattr_family(FontObject* self, void* closure) { - PyObject* res; - - res = Py_FindMethod(font_methods, (PyObject*) self, name); - - if (res) - return res; - - PyErr_Clear(); - - /* attributes */ - if (!strcmp(name, "family")) { - if (self->face->family_name) - return PyString_FromString(self->face->family_name); - Py_RETURN_NONE; - } - if (!strcmp(name, "style")) { - if (self->face->style_name) - return PyString_FromString(self->face->style_name); - Py_RETURN_NONE; - } - if (!strcmp(name, "ascent")) - return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender)); - if (!strcmp(name, "descent")) - return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender)); - - if (!strcmp(name, "glyphs")) - /* number of glyphs provided by this font */ - return PyInt_FromLong(self->face->num_glyphs); - - PyErr_SetString(PyExc_AttributeError, name); - return NULL; +#if PY_VERSION_HEX >= 0x03000000 + if (self->face->family_name) + return PyUnicode_FromString(self->face->family_name); +#else + if (self->face->family_name) + return PyString_FromString(self->face->family_name); +#endif + Py_RETURN_NONE; } -statichere PyTypeObject Font_Type = { - PyObject_HEAD_INIT(NULL) - 0, "Font", sizeof(FontObject), 0, +static PyObject* +font_getattr_style(FontObject* self, void* closure) +{ +#if PY_VERSION_HEX >= 0x03000000 + if (self->face->style_name) + return PyUnicode_FromString(self->face->style_name); +#else + if (self->face->style_name) + return PyString_FromString(self->face->style_name); +#endif + Py_RETURN_NONE; +} + +static PyObject* +font_getattr_ascent(FontObject* self, void* closure) +{ + return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender)); +} + +static PyObject* +font_getattr_descent(FontObject* self, void* closure) +{ + return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender)); +} + +static PyObject* +font_getattr_glyphs(FontObject* self, void* closure) +{ + return PyInt_FromLong(self->face->num_glyphs); +} + +static struct PyGetSetDef font_getsetters[] = { + { "family", (getter) font_getattr_family }, + { "style", (getter) font_getattr_style }, + { "ascent", (getter) font_getattr_ascent }, + { "descent", (getter) font_getattr_descent }, + { "glyphs", (getter) font_getattr_glyphs }, + { NULL } +}; + +static PyTypeObject Font_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "Font", sizeof(FontObject), 0, /* methods */ (destructor)font_dealloc, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)font_getattr, /* tp_getattr */ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + font_methods, /*tp_methods*/ + 0, /*tp_members*/ + font_getsetters, /*tp_getset*/ }; static PyMethodDef _functions[] = { @@ -470,33 +493,58 @@ static PyMethodDef _functions[] = { {NULL, NULL} }; -DL_EXPORT(void) -init_imagingft(void) -{ - PyObject* m; +static int +setup_module(PyObject* m) { PyObject* d; PyObject* v; int major, minor, patch; - /* Patch object type */ - Font_Type.ob_type = &PyType_Type; - - m = Py_InitModule("_imagingft", _functions); d = PyModule_GetDict(m); + /* Ready object type */ + PyType_Ready(&Font_Type); + if (FT_Init_FreeType(&library)) - return; /* leave it uninitalized */ + return 0; /* leave it uninitalized */ FT_Library_Version(library, &major, &minor, &patch); -#if PY_VERSION_HEX >= 0x02020000 - v = PyString_FromFormat("%d.%d.%d", major, minor, patch); +#if PY_VERSION_HEX >= 0x03000000 + v = PyUnicode_FromFormat("%d.%d.%d", major, minor, patch); #else - { - char buffer[100]; - sprintf(buffer, "%d.%d.%d", major, minor, patch); - v = PyString_FromString(buffer); - } + v = PyString_FromFormat("%d.%d.%d", major, minor, patch); #endif PyDict_SetItemString(d, "freetype2_version", v); + + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingft(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingft", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + _functions, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imagingft(void) +{ + PyObject* m = Py_InitModule("_imagingft", _functions); + setup_module(m); +} +#endif + diff --git a/_imagingmath.c b/_imagingmath.c index 928986bb3..c21dac1de 100644 --- a/_imagingmath.c +++ b/_imagingmath.c @@ -16,6 +16,7 @@ #include "Python.h" #include "Imaging.h" +#include "py3.h" #include "math.h" #include "float.h" @@ -227,14 +228,9 @@ install(PyObject *d, char* name, void* value) Py_XDECREF(v); } -DL_EXPORT(void) -init_imagingmath(void) -{ - PyObject* m; - PyObject* d; - - m = Py_InitModule("_imagingmath", _functions); - d = PyModule_GetDict(m); +static int +setup_module(PyObject* m) { + PyObject* d = PyModule_GetDict(m); install(d, "abs_I", abs_I); install(d, "neg_I", neg_I); @@ -281,4 +277,35 @@ init_imagingmath(void) install(d, "gt_F", gt_F); install(d, "ge_F", ge_F); + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingmath(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingmath", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + _functions, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imagingmath(void) +{ + PyObject* m = Py_InitModule("_imagingmath", _functions); + setup_module(m); +} +#endif + diff --git a/_imagingtk.c b/_imagingtk.c index 6165a2acb..b29cfdca8 100644 --- a/_imagingtk.c +++ b/_imagingtk.c @@ -63,8 +63,24 @@ static PyMethodDef functions[] = { {NULL, NULL} /* sentinel */ }; -DL_EXPORT(void) +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingtk(void) { + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingtk", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + functions, /* m_methods */ + }; + + return PyModule_Create(&module_def); +} +#else +PyMODINIT_FUNC init_imagingtk(void) { Py_InitModule("_imagingtk", functions); } +#endif + diff --git a/decode.c b/decode.c index 6ea8d9c3f..9ecfee346 100644 --- a/decode.c +++ b/decode.c @@ -31,12 +31,8 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" +#include "py3.h" #include "Gif.h" #include "Lzw.h" @@ -57,7 +53,7 @@ typedef struct { PyObject* lock; } ImagingDecoderObject; -staticforward PyTypeObject ImagingDecoderType; +static PyTypeObject ImagingDecoderType; static ImagingDecoderObject* PyImaging_DecoderNew(int contextsize) @@ -65,7 +61,8 @@ PyImaging_DecoderNew(int contextsize) ImagingDecoderObject *decoder; void *context; - ImagingDecoderType.ob_type = &PyType_Type; + if(PyType_Ready(&ImagingDecoderType) < 0) + return NULL; decoder = PyObject_New(ImagingDecoderObject, &ImagingDecoderType); if (decoder == NULL) @@ -110,7 +107,7 @@ _decode(ImagingDecoderObject* decoder, PyObject* args) UINT8* buffer; int bufsize, status; - if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize)) + if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize)) return NULL; status = decoder->decode(decoder->im, &decoder->state, buffer, bufsize); @@ -185,26 +182,38 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_getattr(ImagingDecoderObject* self, char* name) -{ - return Py_FindMethod(methods, (PyObject*) self, name); -} - -statichere PyTypeObject ImagingDecoderType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject ImagingDecoderType = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingDecoder", /*tp_name*/ sizeof(ImagingDecoderObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; /* -------------------------------------------------------------------- */ diff --git a/display.c b/display.c index 1a08fadd5..3751b0a2b 100644 --- a/display.c +++ b/display.c @@ -25,12 +25,8 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" +#include "py3.h" /* -------------------------------------------------------------------- */ /* Windows DIB support */ @@ -44,13 +40,16 @@ typedef struct { ImagingDIB dib; } ImagingDisplayObject; -staticforward PyTypeObject ImagingDisplayType; +static PyTypeObject ImagingDisplayType; static ImagingDisplayObject* _new(const char* mode, int xsize, int ysize) { ImagingDisplayObject *display; + if (PyType_Ready(&ImagingDisplayType) < 0) + return NULL; + display = PyObject_New(ImagingDisplayObject, &ImagingDisplayType); if (display == NULL) return NULL; @@ -176,12 +175,18 @@ _releasedc(ImagingDisplayObject* display, PyObject* args) } static PyObject* -_fromstring(ImagingDisplayObject* display, PyObject* args) +_frombytes(ImagingDisplayObject* display, PyObject* args) { char* ptr; int bytes; + +#if PY_VERSION_HEX >= 0x03000000 + if (!PyArg_ParseTuple(args, "y#:frombytes", &ptr, &bytes)) + return NULL; +#else if (!PyArg_ParseTuple(args, "s#:fromstring", &ptr, &bytes)) - return NULL; + return NULL; +#endif if (display->dib->ysize * display->dib->linesize != bytes) { PyErr_SetString(PyExc_ValueError, "wrong size"); @@ -195,12 +200,17 @@ _fromstring(ImagingDisplayObject* display, PyObject* args) } static PyObject* -_tostring(ImagingDisplayObject* display, PyObject* args) +_tobytes(ImagingDisplayObject* display, PyObject* args) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyArg_ParseTuple(args, ":tobytes")) + return NULL; +#else if (!PyArg_ParseTuple(args, ":tostring")) - return NULL; + return NULL; +#endif - return PyString_FromStringAndSize( + return PyBytes_FromStringAndSize( display->dib->bits, display->dib->ysize * display->dib->linesize ); } @@ -212,42 +222,65 @@ static struct PyMethodDef methods[] = { {"query_palette", (PyCFunction)_query_palette, 1}, {"getdc", (PyCFunction)_getdc, 1}, {"releasedc", (PyCFunction)_releasedc, 1}, - {"fromstring", (PyCFunction)_fromstring, 1}, - {"tostring", (PyCFunction)_tostring, 1}, + {"frombytes", (PyCFunction)_frombytes, 1}, + {"tobytes", (PyCFunction)_tobytes, 1}, +#if PY_VERSION_HEX < 0x03000000 + {"fromstring", (PyCFunction)_frombytes, 1}, + {"tostring", (PyCFunction)_tobytes, 1}, +#endif {NULL, NULL} /* sentinel */ }; -static PyObject* -_getattr(ImagingDisplayObject* self, char* name) +static PyObject* +_getattr_mode(ImagingDisplayObject* self, void* closure) { - PyObject* res; - - res = Py_FindMethod(methods, (PyObject*) self, name); - if (res) - return res; - PyErr_Clear(); - if (!strcmp(name, "mode")) return Py_BuildValue("s", self->dib->mode); - if (!strcmp(name, "size")) - return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize); - PyErr_SetString(PyExc_AttributeError, name); - return NULL; } -statichere PyTypeObject ImagingDisplayType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyObject* +_getattr_size(ImagingDisplayObject* self, void* closure) +{ + return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize); +} + +static struct PyGetSetDef getsetters[] = { + { "mode", (getter) _getattr_mode }, + { "size", (getter) _getattr_size }, + { NULL } +}; + +static PyTypeObject ImagingDisplayType = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingDisplay", /*tp_name*/ sizeof(ImagingDisplayObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_delete, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + getsetters, /*tp_getset*/ }; PyObject* @@ -749,7 +782,7 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args) int datasize; int width, height; int x0, y0, x1, y1; - if (!PyArg_ParseTuple(args, "s#(ii)(iiii):_load", &data, &datasize, + if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"(ii)(iiii):_load", &data, &datasize, &width, &height, &x0, &x1, &y0, &y1)) return NULL; diff --git a/docs/pythondoc-PIL.Image.rst b/docs/pythondoc-PIL.Image.rst index 081711aaa..1c2882374 100644 --- a/docs/pythondoc-PIL.Image.rst +++ b/docs/pythondoc-PIL.Image.rst @@ -33,11 +33,11 @@ The PIL.Image Module **frombuffer(mode, size, data, decoder\_name="raw", \*args)** [`# <#PIL.Image.frombuffer-function>`_] - (New in 1.1.4) Creates an image memory from pixel data in a string - or byte buffer. + (New in 1.1.4) Creates an image memory referencing pixel data in a + byte buffer. This function is similar to - `**fromstring** <#PIL.Image.fromstring-function>`_, but uses data in + `**frombytes** <#PIL.Image.frombytes-function>`_, but uses data in the byte buffer, where possible. This means that changes to the original buffer object are reflected in this image). Not all modes can share memory; support modes include "L", "RGBX", "RGBA", and @@ -56,9 +56,9 @@ The PIL.Image Module *\*args* Returns: -**fromstring(mode, size, data, decoder\_name="raw", \*args)** -[`# <#PIL.Image.fromstring-function>`_] - Creates an image memory from pixel data in a string. +**frombytes(mode, size, data, decoder\_name="raw", \*args)** +[`# <#PIL.Image.frombytes-function>`_] + Creates a copy of an image memory from pixel data in a buffer. In its simplest form, this function takes three arguments (mode, size, and unpacked pixel data). @@ -116,7 +116,6 @@ The PIL.Image Module **isDirectory(f)** [`# <#PIL.Image.isDirectory-function>`_] **isImageType(t)** [`# <#PIL.Image.isImageType-function>`_] **isStringType(t)** [`# <#PIL.Image.isStringType-function>`_] -**isTupleType(t)** [`# <#PIL.Image.isTupleType-function>`_] **merge(mode, bands)** [`# <#PIL.Image.merge-function>`_] *mode* @@ -234,14 +233,16 @@ The Image Class *filter* Returns: -**fromstring(data, decoder\_name="raw", \*args)** -[`# <#PIL.Image.Image.fromstring-method>`_] - Loads this image with pixel data from a string. +**frombytes(data, decoder\_name="raw", \*args)** +[`# <#PIL.Image.Image.frombytes-method>`_] + Loads this image with pixel data from a byte uffer. This method is similar to the - `**fromstring** <#PIL.Image.fromstring-function>`_ function, but + `**frombytes** <#PIL.Image.frombytes-function>`_ function, but loads data into this image instead of creating a new image object. + (In Python 2.6 and 2.7, this is also available as fromstring().) + **getbands()** [`# <#PIL.Image.Image.getbands-method>`_] Returns a tuple containing the name of each band in this image. For example, **getbands** on an RGB image returns ("R", "G", "B"). @@ -517,8 +518,9 @@ The Image Class Returns: Raises **ValueError**: -**tostring(encoder\_name="raw", \*args)** -[`# <#PIL.Image.Image.tostring-method>`_] +**tobytes(encoder\_name="raw", \*args)** +[`# <#PIL.Image.Image.tobytes-method>`_] + (In Python 2.6 and 2.7, this is also available as tostring().) *encoder\_name* *\*args* diff --git a/docs/pythondoc-PIL.ImageWin.rst b/docs/pythondoc-PIL.ImageWin.rst index 05558a3a5..2bcb8fbb2 100644 --- a/docs/pythondoc-PIL.ImageWin.rst +++ b/docs/pythondoc-PIL.ImageWin.rst @@ -58,11 +58,12 @@ The Dib Class instance. In PythonWin, you can use the **GetHandleAttrib** method of the **CDC** class to get a suitable handle. -**fromstring(buffer)** [`# <#PIL.ImageWin.Dib.fromstring-method>`_] +**frombytes(buffer)** [`# <#PIL.ImageWin.Dib.frombytes-method>`_] + (For Python 2.6/2.7, this is also available as fromstring(buffer).) *buffer* - A string buffer containing display data (usually data returned - from **tostring**) + A byte buffer containing display data (usually data returned + from **tobytes**) **paste(im, box=None)** [`# <#PIL.ImageWin.Dib.paste-method>`_] @@ -82,7 +83,7 @@ The Dib Class *handle* Returns: -**tostring()** [`# <#PIL.ImageWin.Dib.tostring-method>`_] +**tobytes()** [`# <#PIL.ImageWin.Dib.tobytes-method>`_] Returns: diff --git a/encode.c b/encode.c index 2ae13ad72..479da7d51 100644 --- a/encode.c +++ b/encode.c @@ -24,12 +24,8 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" +#include "py3.h" #include "Gif.h" #ifdef HAVE_UNISTD_H @@ -49,7 +45,7 @@ typedef struct { PyObject* lock; } ImagingEncoderObject; -staticforward PyTypeObject ImagingEncoderType; +static PyTypeObject ImagingEncoderType; static ImagingEncoderObject* PyImaging_EncoderNew(int contextsize) @@ -57,7 +53,8 @@ PyImaging_EncoderNew(int contextsize) ImagingEncoderObject *encoder; void *context; - ImagingEncoderType.ob_type = &PyType_Type; + if(!PyType_Ready(&ImagingEncoderType) < 0) + return NULL; encoder = PyObject_New(ImagingEncoderObject, &ImagingEncoderType); if (encoder == NULL) @@ -110,15 +107,15 @@ _encode(ImagingEncoderObject* encoder, PyObject* args) if (!PyArg_ParseTuple(args, "|i", &bufsize)) return NULL; - buf = PyString_FromStringAndSize(NULL, bufsize); + buf = PyBytes_FromStringAndSize(NULL, bufsize); if (!buf) return NULL; status = encoder->encode(encoder->im, &encoder->state, - (UINT8*) PyString_AsString(buf), bufsize); + (UINT8*) PyBytes_AsString(buf), bufsize); /* adjust string length to avoid slicing in encoder */ - if (_PyString_Resize(&buf, (status > 0) ? status : 0) < 0) + if (_PyBytes_Resize(&buf, (status > 0) ? status : 0) < 0) return NULL; result = Py_BuildValue("iiO", status, encoder->state.errcode, buf); @@ -241,26 +238,38 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_getattr(ImagingEncoderObject* self, char* name) -{ - return Py_FindMethod(methods, (PyObject*) self, name); -} - -statichere PyTypeObject ImagingEncoderType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject ImagingEncoderType = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingEncoder", /*tp_name*/ sizeof(ImagingEncoderObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; /* -------------------------------------------------------------------- */ @@ -438,9 +447,21 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args) int optimize = 0; char* dictionary = NULL; int dictionary_size = 0; - if (!PyArg_ParseTuple(args, "ss|is#", &mode, &rawmode, &optimize, - &dictionary, &dictionary_size)) - return NULL; + if (!PyArg_ParseTuple(args, "ss|i"PY_ARG_BYTES_LENGTH, &mode, &rawmode, + &optimize, &dictionary, &dictionary_size)) + return NULL; + + /* Copy to avoid referencing Python's memory, but there's no mechanism to + free this memory later, so this function (and several others here) + leaks. */ + if (dictionary && dictionary_size > 0) { + char* p = malloc(dictionary_size); + if (!p) + return PyErr_NoMemory(); + memcpy(p, dictionary, dictionary_size); + dictionary = p; + } else + dictionary = NULL; encoder = PyImaging_EncoderNew(sizeof(ZIPSTATE)); if (encoder == NULL) @@ -500,8 +521,9 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) int xdpi = 0, ydpi = 0; int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */ char* extra = NULL; int extra_size; - if (!PyArg_ParseTuple(args, "ss|iiiiiiiis#", &mode, &rawmode, &quality, - &progressive, &smooth, &optimize, &streamtype, + if (!PyArg_ParseTuple(args, "ss|iiiiiiii"PY_ARG_BYTES_LENGTH, + &mode, &rawmode, &quality, + &progressive, &smooth, &optimize, &streamtype, &xdpi, &ydpi, &subsampling, &extra, &extra_size)) return NULL; diff --git a/libImaging/Quant.c b/libImaging/Quant.c index d25ba05dd..f7d9bf1ac 100644 --- a/libImaging/Quant.c +++ b/libImaging/Quant.c @@ -151,7 +151,7 @@ rehash_collide(HashTable h, void *newkey, void *newval) { - *valp=(void *)((*(int *)valp)+(*(int *)&newval)); + *valp = (void *)(((int) *valp) + ((int) newval)); } /* %% */ @@ -244,7 +244,7 @@ hash_to_list(HashTable h, const void *key, const void *val, void *u) Pixel *pixel=(Pixel *)&key; int i; Pixel q; - int count=*(int *)&val; + int count=(int) val; PIXEL_SCALE(pixel,&q,d->scale); diff --git a/map.c b/map.c index beba8a242..5bd601ba3 100644 --- a/map.c +++ b/map.c @@ -20,11 +20,6 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" #ifdef WIN32 @@ -39,9 +34,11 @@ #include "windows.h" #endif +#include "py3.h" + /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); -extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr); +extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); /* -------------------------------------------------------------------- */ /* Standard mapper */ @@ -57,14 +54,15 @@ typedef struct { #endif } ImagingMapperObject; -staticforward PyTypeObject ImagingMapperType; +static PyTypeObject ImagingMapperType; ImagingMapperObject* PyImaging_MapperNew(const char* filename, int readonly) { ImagingMapperObject *mapper; - ImagingMapperType.ob_type = &PyType_Type; + if (PyType_Ready(&ImagingMapperType) < 0) + return NULL; mapper = PyObject_New(ImagingMapperObject, &ImagingMapperType); if (mapper == NULL) @@ -147,12 +145,12 @@ mapping_read(ImagingMapperObject* mapper, PyObject* args) if (size < 0) size = 0; - buf = PyString_FromStringAndSize(NULL, size); + buf = PyBytes_FromStringAndSize(NULL, size); if (!buf) return NULL; if (size > 0) { - memcpy(PyString_AsString(buf), mapper->base + mapper->offset, size); + memcpy(PyBytes_AsString(buf), mapper->base + mapper->offset, size); mapper->offset += size; } @@ -260,26 +258,38 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -mapping_getattr(ImagingMapperObject* self, char* name) -{ - return Py_FindMethod(methods, (PyObject*) self, name); -} - -statichere PyTypeObject ImagingMapperType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject ImagingMapperType = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingMapper", /*tp_name*/ sizeof(ImagingMapperObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)mapping_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)mapping_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; PyObject* @@ -298,6 +308,7 @@ PyImaging_Mapper(PyObject* self, PyObject* args) typedef struct ImagingBufferInstance { struct ImagingMemoryInstance im; PyObject* target; + Py_buffer view; } ImagingBufferInstance; static void @@ -305,6 +316,7 @@ mapping_destroy_buffer(Imaging im) { ImagingBufferInstance* buffer = (ImagingBufferInstance*) im; + PyBuffer_Release(&buffer->view); Py_XDECREF(buffer->target); } @@ -313,10 +325,9 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) { int y, size; Imaging im; - char* ptr; - int bytes; PyObject* target; + Py_buffer view; char* mode; char* codec; PyObject* bbox; @@ -346,12 +357,14 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) size = ysize * stride; /* check buffer size */ - bytes = PyImaging_ReadBuffer(target, (const void**) &ptr); - if (bytes < 0) { + if (PyImaging_GetBuffer(target, &view) < 0) + return NULL; + + if (view.len < 0) { PyErr_SetString(PyExc_ValueError, "buffer has negative size"); return NULL; } - if (offset + size > bytes) { + if (offset + size > view.len) { PyErr_SetString(PyExc_ValueError, "buffer is not large enough"); return NULL; } @@ -365,15 +378,16 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) /* setup file pointers */ if (ystep > 0) for (y = 0; y < ysize; y++) - im->image[y] = ptr + offset + y * stride; + im->image[y] = (char*)view.buf + offset + y * stride; else for (y = 0; y < ysize; y++) - im->image[ysize-y-1] = ptr + offset + y * stride; + im->image[ysize-y-1] = (char*)view.buf + offset + y * stride; im->destroy = mapping_destroy_buffer; Py_INCREF(target); ((ImagingBufferInstance*) im)->target = target; + ((ImagingBufferInstance*) im)->view = view; if (!ImagingNewEpilogue(im)) return NULL; diff --git a/outline.c b/outline.c index 6bb2ebb54..25e63aeaf 100644 --- a/outline.c +++ b/outline.c @@ -19,11 +19,6 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" @@ -35,15 +30,18 @@ typedef struct { ImagingOutline outline; } OutlineObject; -staticforward PyTypeObject OutlineType; +static PyTypeObject OutlineType; -#define PyOutline_Check(op) ((op)->ob_type == &OutlineType) +#define PyOutline_Check(op) (Py_TYPE(op) == &OutlineType) static OutlineObject* _outline_new(void) { OutlineObject *self; + if (PyType_Ready(&OutlineType) < 0) + return NULL; + self = PyObject_New(OutlineObject, &OutlineType); if (self == NULL) return NULL; @@ -159,21 +157,36 @@ static struct PyMethodDef _outline_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_outline_getattr(OutlineObject* self, char* name) -{ - return Py_FindMethod(_outline_methods, (PyObject*) self, name); -} - -statichere PyTypeObject OutlineType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject OutlineType = { + PyVarObject_HEAD_INIT(NULL, 0) "Outline", /*tp_name*/ sizeof(OutlineObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_outline_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_outline_getattr, /*tp_getattr*/ - 0 /*tp_setattr*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _outline_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; diff --git a/path.c b/path.c index f083b971d..622edb10a 100644 --- a/path.c +++ b/path.c @@ -30,23 +30,11 @@ #include -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - -#if PY_VERSION_HEX < 0x02050000 -#define Py_ssize_t int -#define lenfunc inquiry -#define ssizeargfunc intargfunc -#define ssizessizeargfunc intintargfunc -#define ssizeobjargproc intobjargproc -#define ssizessizeobjargproc intintobjargproc -#endif +#include "py3.h" /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); -extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr); +extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); /* -------------------------------------------------------------------- */ /* Class */ @@ -59,7 +47,7 @@ typedef struct { int index; /* temporary use, e.g. in decimate */ } PyPathObject; -staticforward PyTypeObject PyPathType; +static PyTypeObject PyPathType; static double* alloc_array(int count) @@ -89,6 +77,9 @@ path_new(Py_ssize_t count, double* xy, int duplicate) xy = p; } + if (PyType_Ready(&PyPathType) < 0) + return NULL; + path = PyObject_New(PyPathObject, &PyPathType); if (path == NULL) return NULL; @@ -110,7 +101,7 @@ path_dealloc(PyPathObject* path) /* Helpers */ /* -------------------------------------------------------------------- */ -#define PyPath_Check(op) ((op)->ob_type == &PyPathType) +#define PyPath_Check(op) (Py_TYPE(op) == &PyPathType) int PyPath_Flatten(PyObject* data, double **pxy) @@ -131,16 +122,20 @@ PyPath_Flatten(PyObject* data, double **pxy) if (PyImaging_CheckBuffer(data)) { /* Assume the buffer contains floats */ - float* ptr; - int n = PyImaging_ReadBuffer(data, (const void**) &ptr); - n /= 2 * sizeof(float); - xy = alloc_array(n); - if (!xy) - return -1; - for (i = 0; i < n+n; i++) - xy[i] = ptr[i]; - *pxy = xy; - return n; + Py_buffer buffer; + if (PyImaging_GetBuffer(data, &buffer) == 0) { + int n = buffer.len / (2 * sizeof(float)); + float *ptr = (float*) buffer.buf; + xy = alloc_array(n); + if (!xy) + return -1; + for (i = 0; i < n+n; i++) + xy[i] = ptr[i]; + *pxy = xy; + PyBuffer_Release(&buffer); + return n; + } + PyErr_Clear(); } if (!PySequence_Check(data)) { @@ -361,6 +356,8 @@ path_getbbox(PyPathObject* self, PyObject* args) static PyObject* path_getitem(PyPathObject* self, int i) { + if (i < 0) + i = self->count + i; if (i < 0 || i >= self->count) { PyErr_SetString(PyExc_IndexError, "path index out of range"); return NULL; @@ -539,22 +536,56 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -path_getattr(PyPathObject* self, char* name) +static PyObject* +path_getattr_id(PyPathObject* self, void* closure) { - PyObject* res; - - res = Py_FindMethod(methods, (PyObject*) self, name); - if (res) - return res; - - PyErr_Clear(); - - if (strcmp(name, "id") == 0) return Py_BuildValue("l", (long) self->xy); +} - PyErr_SetString(PyExc_AttributeError, name); - return NULL; +static struct PyGetSetDef getsetters[] = { + { "id", (getter) path_getattr_id }, + { NULL } +}; + +static PyObject* +path_subscript(PyPathObject* self, PyObject* item) { + if (PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return NULL; + return path_getitem(self, i); + } + if (PySlice_Check(item)) { + int len = 4; + Py_ssize_t start, stop, step, slicelength; + +#if PY_VERSION_HEX >= 0x03000000 + if (PySlice_GetIndicesEx(item, len, &start, &stop, &step, &slicelength) < 0) + return NULL; +#else + if (PySlice_GetIndicesEx((PySliceObject*)item, len, &start, &stop, &step, &slicelength) < 0) + return NULL; +#endif + + if (slicelength <= 0) { + double *xy = alloc_array(0); + return (PyObject*) path_new(0, xy, 0); + } + else if (step == 1) { + return path_getslice(self, start, stop); + } + else { + PyErr_SetString(PyExc_TypeError, "slice steps not supported"); + return NULL; + } + } + else { + PyErr_Format(PyExc_TypeError, + "Path indices must be integers, not %.200s", + Py_TYPE(&item)->tp_name); + return NULL; + } } static PySequenceMethods path_as_sequence = { @@ -567,21 +598,43 @@ static PySequenceMethods path_as_sequence = { (ssizessizeobjargproc)0, /*sq_ass_slice*/ }; -statichere PyTypeObject PyPathType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyMappingMethods path_as_mapping = { + (lenfunc)path_len, + (binaryfunc)path_subscript, + NULL +}; + +static PyTypeObject PyPathType = { + PyVarObject_HEAD_INIT(NULL, 0) "Path", /*tp_name*/ sizeof(PyPathObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)path_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)path_getattr, /*tp_getattr*/ + 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number */ &path_as_sequence, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0, /*tp_hash*/ + &path_as_mapping, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + getsetters, /*tp_getset*/ }; + diff --git a/py3.h b/py3.h new file mode 100644 index 000000000..e0658b0bb --- /dev/null +++ b/py3.h @@ -0,0 +1,51 @@ +/* + Python3 definition file to consistently map the code to Python 2.6 or + Python 3. + + PyInt and PyLong were merged into PyLong in Python 3, so all PyInt functions + are mapped to PyLong. + + PyString, on the other hand, was split into PyBytes and PyUnicode. We map + both back onto PyString, so use PyBytes or PyUnicode where appropriate. The + only exception to this is _imagingft.c, where PyUnicode is left alone. +*/ + +#if PY_VERSION_HEX >= 0x03000000 +#define PY_ARG_BYTES_LENGTH "y#" + +/* Map PyInt -> PyLong */ +#define PyInt_AsLong PyLong_AsLong +#define PyInt_Check PyLong_Check +#define PyInt_FromLong PyLong_FromLong +#define PyInt_AS_LONG PyLong_AS_LONG + +#else /* PY_VERSION_HEX < 0x03000000 */ +#define PY_ARG_BYTES_LENGTH "s#" + +#if !defined(KEEP_PY_UNICODE) +/* Map PyUnicode -> PyString */ +#undef PyUnicode_AsString +#undef PyUnicode_AS_STRING +#undef PyUnicode_Check +#undef PyUnicode_FromStringAndSize +#undef PyUnicode_FromString +#undef PyUnicode_FromFormat + +#define PyUnicode_AsString PyString_AsString +#define PyUnicode_AS_STRING PyString_AS_STRING +#define PyUnicode_Check PyString_Check +#define PyUnicode_FromStringAndSize PyString_FromStringAndSize +#define PyUnicode_FromString PyString_FromString +#define PyUnicode_FromFormat PyString_FromFormat +#endif + +/* Map PyBytes -> PyString */ +#define PyBytes_AsString PyString_AsString +#define PyBytes_AS_STRING PyString_AS_STRING +#define PyBytes_Check PyString_Check +#define PyBytes_FromStringAndSize PyString_FromStringAndSize +#define PyBytes_FromString PyString_FromString +#define _PyBytes_Resize _PyString_Resize + +#endif /* PY_VERSION_HEX < 0x03000000 */ + diff --git a/selftest.py b/selftest.py index 22bbe433b..b6d7a300c 100644 --- a/selftest.py +++ b/selftest.py @@ -1,5 +1,5 @@ # minimal sanity check - +from __future__ import print_function ROOT = "." import os, sys @@ -12,8 +12,8 @@ from PIL import ImageMath try: Image.core.ping -except ImportError, v: - print "***", v +except ImportError as v: + print("***", v) sys.exit() except AttributeError: pass @@ -49,19 +49,19 @@ def testimage(): ('PPM', 'RGB', (128, 128)) >>> try: ... _info(Image.open(os.path.join(ROOT, "Images/lena.jpg"))) - ... except IOError, v: - ... print v + ... except IOError as v: + ... print(v) ('JPEG', 'RGB', (128, 128)) PIL doesn't actually load the image data until it's needed, or you call the "load" method: >>> im = Image.open(os.path.join(ROOT, "Images/lena.ppm")) - >>> print im.im # internal image attribute + >>> print(im.im) # internal image attribute None >>> a = im.load() - >>> type(im.im) - + >>> type(im.im) # doctest: +ELLIPSIS + <... '...ImagingCore'> You can apply many different operations on images. Most operations return a new image: @@ -89,17 +89,17 @@ def testimage(): 2 >>> len(im.histogram()) 768 - >>> _info(im.point(range(256)*3)) + >>> _info(im.point(list(range(256))*3)) (None, 'RGB', (128, 128)) >>> _info(im.resize((64, 64))) (None, 'RGB', (64, 64)) >>> _info(im.rotate(45)) (None, 'RGB', (128, 128)) - >>> map(_info, im.split()) + >>> [_info(ch) for ch in im.split()] [(None, 'L', (128, 128)), (None, 'L', (128, 128)), (None, 'L', (128, 128))] >>> len(im.convert("1").tobitmap()) 10456 - >>> len(im.tostring()) + >>> len(im.tobytes()) 49152 >>> _info(im.transform((512, 512), Image.AFFINE, (1,0,0,0,1,0))) (None, 'RGB', (512, 512)) @@ -159,15 +159,15 @@ def check_module(feature, module): try: __import__("PIL." + module) except ImportError: - print "***", feature, "support not installed" + print("***", feature, "support not installed") else: - print "---", feature, "support ok" + print("---", feature, "support ok") def check_codec(feature, codec): if codec + "_encoder" not in dir(Image.core): - print "***", feature, "support not installed" + print("***", feature, "support not installed") else: - print "---", feature, "support ok" + print("---", feature, "support ok") if __name__ == "__main__": @@ -175,28 +175,28 @@ if __name__ == "__main__": exit_status = 0 - print "-"*68 - print "PIL", Image.VERSION, "TEST SUMMARY " - print "-"*68 - print "Python modules loaded from", os.path.dirname(Image.__file__) - print "Binary modules loaded from", os.path.dirname(Image.core.__file__) - print "-"*68 + print("-"*68) + print("PIL", Image.VERSION, "TEST SUMMARY ") + print("-"*68) + print("Python modules loaded from", os.path.dirname(Image.__file__)) + print("Binary modules loaded from", os.path.dirname(Image.core.__file__)) + print("-"*68) check_module("PIL CORE", "_imaging") check_module("TKINTER", "_imagingtk") check_codec("JPEG", "jpeg") check_codec("ZLIB (PNG/ZIP)", "zip") check_module("FREETYPE2", "_imagingft") check_module("LITTLECMS", "_imagingcms") - print "-"*68 + print("-"*68) # use doctest to make sure the test program behaves as documented! import doctest, selftest - print "Running selftest:" + print("Running selftest:") status = doctest.testmod(selftest) if status[0]: - print "*** %s tests of %d failed." % status + print("*** %s tests of %d failed." % status) exit_status = 1 else: - print "--- %s tests passed." % status[1] + print("--- %s tests passed." % status[1]) sys.exit(exit_status) diff --git a/setup.py b/setup.py index c9b69573a..239b4676c 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,4 @@ +from __future__ import print_function import glob import os import platform @@ -433,7 +434,7 @@ class pil_build_ext(build_ext): tmpfile) try: if ret >> 8 == 0: - fp = open(tmpfile, 'rb') + fp = open(tmpfile, 'r') multiarch_path_component = fp.readline().strip() _add_directory(self.compiler.library_dirs, '/usr/lib/' + multiarch_path_component) @@ -453,7 +454,6 @@ setup( author='Alex Clark (fork author)', author_email='aclark@aclark.net', url='http://github.com/python-imaging/Pillow', - use_2to3=True, classifiers=[ "Development Status :: 6 - Mature", "Topic :: Multimedia :: Graphics", @@ -462,6 +462,12 @@ setup( "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture", "Topic :: Multimedia :: Graphics :: Graphics Conversion", "Topic :: Multimedia :: Graphics :: Viewers", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.2", + "Programming Language :: Python :: 3.3", ], cmdclass={"build_ext": pil_build_ext}, ext_modules=[Extension("_imaging", ["_imaging.c"])], diff --git a/tox.ini b/tox.ini index 6123e8613..338c94d6b 100644 --- a/tox.ini +++ b/tox.ini @@ -4,19 +4,8 @@ # and then run "tox" from this directory. [tox] -envlist = py25, py26, py27, py32, pypy +envlist = py26, py27, py32, py33, pypy [testenv] commands = - {envpython} setup.py clean - rm -rf build dist - {envpython} setup.py install {envpython} selftest.py - -[testenv:py32] -commands = - {envpython} setup.py clean - rm -rf build dist - {envpython} setup.py install - 2to3 -w -n -o . --add-suffix=3 selftest.py - {envpython} selftest.py3