From 9bada996533a2a925da9fe4c93202604a7ee3210 Mon Sep 17 00:00:00 2001 From: Clinton Ingram Date: Sat, 10 Apr 2021 12:51:54 -0700 Subject: [PATCH] v0.12.0 --- doc/docfx.json | 6 +- doc/images/IMG_2301-ImageSharp.jpg | Bin 4670 -> 4765 bytes doc/images/IMG_2301-MagicScaler.jpg | Bin 4686 -> 4925 bytes doc/images/IMG_2301-NetVips.jpg | Bin 3995 -> 4004 bytes doc/images/IMG_2301-SkiaSharpCanvas.jpg | Bin 3176 -> 3284 bytes doc/images/IMG_2445-ImageSharp.jpg | Bin 4019 -> 4116 bytes doc/images/IMG_2445-MagicScaler.jpg | Bin 3761 -> 4089 bytes doc/images/IMG_2445-NetVips.jpg | Bin 3352 -> 3352 bytes doc/images/IMG_2445-SkiaSharpCanvas.jpg | Bin 2732 -> 2781 bytes doc/images/IMG_2525-ImageSharp.jpg | Bin 4944 -> 5039 bytes doc/images/IMG_2525-MagicScaler.jpg | Bin 4660 -> 4863 bytes doc/images/IMG_2525-NetVips.jpg | Bin 4051 -> 4037 bytes doc/images/IMG_2525-SkiaSharpCanvas.jpg | Bin 3318 -> 3481 bytes doc/images/sample-ImageSharp.jpg | Bin 6811 -> 6809 bytes doc/images/sample-NetVips.jpg | Bin 3608 -> 3620 bytes doc/images/sample-SkiaSharpCanvas.jpg | Bin 3240 -> 3246 bytes readme.md | 247 ++++++++++++----------- src/MagicScaler/Magic/OctreeQuantizer.cs | 8 +- src/WebRSize/WebRSize.csproj | 2 +- 19 files changed, 137 insertions(+), 126 deletions(-) diff --git a/doc/docfx.json b/doc/docfx.json index 2bee4b12..796c3697 100644 --- a/doc/docfx.json +++ b/doc/docfx.json @@ -5,13 +5,13 @@ { "src": "../", "files": [ - "out/bin/MagicScaler/Dist/netcoreapp3.0/PhotoSauce.MagicScaler.dll" + "out/bin/MagicScaler/Dist/netcoreapp3.1/PhotoSauce.MagicScaler.dll" ] } ], "dest": "../out/doc/api", "properties": { - "TargetFramework": "netcoreapp3.0", + "TargetFramework": "netcoreapp3.1", "Configuration": "Dist" } } @@ -35,7 +35,7 @@ "markdownEngineName": "markdig", "postProcessors": "ExtractSearchIndex", "globalMetadata": { - "_appFooter": "Copyright © 2020 Clinton Ingram
Generated with DocFX
", + "_appFooter": "Copyright © 2021 Clinton Ingram
Generated with DocFX
", "_appTitle": "PhotoSauce Documentation", "_enableSearch": true, "_enableNewTab": false, diff --git a/doc/images/IMG_2301-ImageSharp.jpg b/doc/images/IMG_2301-ImageSharp.jpg index ed6c52df01e7b8ea4ec52b4e53a6a15e974b9b1e..cb1f9836a653082ad6a921d888a9143724bfed35 100644 GIT binary patch delta 3259 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

66R6f`R{IMgxf^NGLZsQ+2?7g gmBg<@L`Kv7SlI9S{JpcRkos5(L>vkpfger%6N3dTUH||9 delta 3192 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 diff --git a/doc/images/IMG_2301-SkiaSharpCanvas.jpg b/doc/images/IMG_2301-SkiaSharpCanvas.jpg index bd5e2e51e818e17e413747052d619eef801222f1..1f6676b950bf5877e87184546b2e7b0fc592fc65 100644 GIT binary patch delta 3117 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 HkTd_;_p8FJ delta 3008 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 CGJ$;n diff --git a/doc/images/IMG_2445-ImageSharp.jpg b/doc/images/IMG_2445-ImageSharp.jpg index 7183465ff5ca205b67118169e8552fab28a15d8d..5c06e821d4a554b0ebc2744205cf32d9a63b3c31 100644 GIT binary patch delta 2743 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 delta 2646 zcmV-c3aRy!AhRE^R|2!N0!sn|m8a&D0tGyON9aXNmF!aLb!VDNbr`E82TDUrMIa+# zNs5q)X%xUrQqTo1C;_FhL2OXUO$?;a1ev2W+9?3)4-|mX&;tt^P}!iZMLfxG8Inf| zgQ+yYn-mj;C(^ksPvRbnr!he(oxtfIt#X=&#EnkzU+fkPkPdfkEyuZ}Gqb$$RLdiO z$77^iT$Nxqv#fEj$56+U>C(BWFT6@GCb@#n!WNQmHw|#iM+z~VWB7klT)oDv;(4HH zj_Be8IpAbfdH(?6BUCRX(E}iVtsP0l7X#8_@qVit0e7Jdxd64j(0mN?a6mmhE3LM< zitYuP7Tk@{;~$~vUJlRVI5v}VPDlfP_n_6we-U`6N|z|biS2;d{3y7Oair%p<+gtm z?`_v+rKOf({AvpRmDg!LJJ5Ats+Wljr-K~CAs?SwV04`*;L}z_l>^4=>UtG96uF=U zz@n2hxu66z8YrLz;);)Pqjdm2qPe<)&fV@VW=LT{mUnCcRCUdE&%^p$m)DJd@{SP) z-jTZjUUxLp>(07|h^0i0mwmOMVq=JQ+y;JQ{?Pvb0ETVr9zM8aRnaV1qvR#k%8;** z?k`sQ*Bv_QTBP6cJJjVdxyXziihlv$>r?6)lyHLg9!$tMSBc0}AMWR`(zy*sUu%~r zq{yJ*8eXID>G+;#1E_>)x?`AsMYL=f9kRI2M{r5#YN7EAHu1@B)2WPP4ZQ9L>G@Y1 zHN^9_W|f%xnuPStS0g6q>087xn^uUl&PtWY9D8@DCGi4D7+)$(!=KWnU4GrJ12as2s-uC7^Xt~R z#fnEqaK9=5Tl2*+mPJL8qhT8os@UM3^!a?nbF4bbJaS1KvAgmS8;(7B?@BFg{>+bf z0g<{D@9Z&LnGB?E1Y~EC+N6T&YhoHFVeC22;YTYS(cWm+ciKFM$kgsFcQ6tB`{N() zANK^k6fz*OAz3V7440$mf|DWPV+%vC?%hsBgBF zXT4%Pu(C8^R34+|AIq&s&tZyGnk14ZSB=KyWjO?TgHm*_Cet;0j{@rHXFRVyhj7^B zhWk67xFC`EXOZt-svH#z2r5QF9SEcYiJ+Q{$S5)jb^vBn#^a2C(9WSZXCvCC*a^uQ zprW1#7^DZ0>zB>qKZt_Nfi0e!kg;PSm}4gxIp_IRuMu6`Y4Ijx)NRnF^0S0xTxXye zAam*~3I71%SMe8CwYWuLG_FSe0R-+G_TbchFL=$Xc}r@CYJ#xN&T*cHKZPrf#}%pS zGF{vj+shx76jZQ(SV%18lo2AYE{{Tv~%3eq<8L*J1`CI$B<-ef%)bYWle&j9km5GaC!5fM7_04PfsSXdr6!}(U8i+T*Pi3$}$7#VU@E_nnFwKcPX%LK7mERiVMw#)M>5AQkYnviL2Z+a$x zNb10x#EPn<@G;N54crp3*LbC^uXGv2FJ**Pl_RxLjM2@fKSy=1XTeULU1-`s8O^Z zs+@}GHN7@#S)Mt5{9h}DKQG`gYZL%FV0|(DDFCSy5L-RPz#;_x1sM!|0Dt5P$h+3A zZZfHuBL3=dPt^4_(zFY8;oFo^mqjNY{ePu#Y`}M_ioUEQYj|D3@g1&D-Yh@y(^>lV zV%Kj~c>VFI{4u$FO=Ncxrc8704L8L}<@k>G{<>%M6=`ZmLoH--*dQ9Q0-reuVjFPA z(~7DADhTI_xnP^`t&sIHs7m1bifj(YRGu4ayIXmXZIwRw>ZdprPHS%_M~@t5w*dWd z*YPzciR|UP*6kyV9g7n0IN$=tq`Yv~PB{5~TE^BiDG2v~m<*%%ldTL_e=u{|n06k5 zy-qb4%N}VwrGJ@-1o~B31EUsFGrkuCKt$h`1zM2pV{6-lNiu4rkA>zaJ5S1%j2 z?V4`s9OU2+YM07E&S~bLTNr)Adx0S(Az~a5zT#h+(yM>N5-HpVB0Pu5M9o3Dj zx`vGNLX8BHgpo+#l1Rz?K%?DbQaY4EKOr7+-XQ}t0kO7{j-iAO1dY^j4f7GwkniTBLC{wdIp-#-= ztdTs%&suXH2Lq;PW5D2a%?x-P4w<9{OOv`6PQ^$+m2i52BTR+I-j3iEKA$qts1h{D zTz%;70aNMoEgFF%OohkZj^GtOpEA;J<4EZB(xNyCMPrx7;hcPjn(^-(DE`@Y@8lu> z0LQUMjud}vf4g_`5dQ$<*rUe^Kek=F`3QgU>{VXvF~P z;yuh!=k%iaK=8arxr#jglwT+w7l`*UN1xJ$^k@wEBLy^7Il~su{q`%Ul|sG+5~x?e zqE!m`6wzIG E*^aI+JOBUy diff --git a/doc/images/IMG_2445-MagicScaler.jpg b/doc/images/IMG_2445-MagicScaler.jpg index acb26095ad19c59181c43f09a8e1f33031bd3b37..abbc75cfe44296931637a36faac356529acd342f 100644 GIT binary patch delta 3494 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 delta 3164 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 C^dmz6 diff --git a/doc/images/IMG_2445-NetVips.jpg b/doc/images/IMG_2445-NetVips.jpg index 9f0b1f878de6ce637e7953e5e9573028e4ecedcf..5928ab890b40ee2bfe1653fb5e1c164bfb76920f 100644 GIT binary patch delta 2746 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 AtpET3 delta 2728 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 delta 2575 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 delta 3599 zcma);_ct31qsK$E#$(n@8d{@9R3BSCMT$}}YbADV8hbQqRE&Cr+N4^0RLz*NwM30r zQ7be;J@%+tEj6m|z32Y$p7)%4&b>c=KA-O&@U4^hBwmlCZz9mU(qq=;>fN@b(dvd> zR;cx*M0a9}ipCQ7J=iF}CLX5WX-DcFt+PQKhZ6gdQ}_g8@XfR34gnD=KuI1AMk-L} zDysOIK8o^wGOpssJ1uf$CW=;2<$M(~(=8kNZL)7|i%4sWK*G%f%$<|mqtkre&W{=D zvZ?UUfgYo@n(s(q$}{hiI?Sf9?;ZgspSYZ7bYMFA9%UNbpy8%|QAA2bVkCj=Z1hg_ zb~dFZj0fCN$Zd7?ouWy_N?X^-suDD->P1xw7F{C$Hp{`x+~fn6s=k3x+Z#4bwd zoenR>?rqs+Z7a1eAaFk(Ag?v*a{D_o+Bugrx5{gLbuYxMMqU)*+yI9oIdg;-07n+$ zq)}C-qGdnMwBO2u<%%lK;`xO2iGq)L(zGw4*PbTH$O`+bKgX;uFX!nP5xrauQF)|^ zcdl_?7-#}gi=4*GjG7eVOv+3d7{aIgQO<96;cKnMMNAJJvR)rovU;dX9+ICVVJmhG zrK1;X;nt{wk34VJT*-jl*uV*}>)iCN|bSv*`w9|#02xlr?5U;QK5 zTBYcgb9Z`3y6WcksK1oVw_V@s-YRmWkBxW5E4|Va2$Tm}gJBR4)zdiF_xO)JP1qT` zP9G*^f@`p<1eFlfq+r@kVFePaW^?i}9|~NoTiwmH7_{JnQ#)pZ!k(syQT>`QJA?{W z=3U3Ra6)fmXVBJUE)GYO@%EhGM$BG2)pScSljpO5+HMszgSXS+mAPCz(a})56%bG< zuSIUqH`({FSckR05%Y|R2-=$t*fEE@@>{7mINj(%^qc^mUhKf06g!NYd0}K8J+^<} znl{l9qiYN&5+UYylt7|0X2W-Z5yLh8sSJnVXvl<2;ymh$m)1hWSZTL_-@(O`dTUWs zH)^$sFOwl`AEqBU<={|MT&|)eJ%--oZ@o&nyM&vtZiJs5s>5tz*z`kfEERhD9(*n4 zhD4{+W?EbSzFt|&^$6aTipfnMvrg`sM=!LBar(ip4-V3lni%Fpx=Ai8l$c1McT(C`xn6a?Jd`jz6uS5LZ}4 zJ%j@`0l-s>-UxEPK09=2G-{ttQQ@W*lE`f1WC@yf zuNY`}c|G*)z#K8q#3_UMqKXZ%3CcH*(nB?r>u2+oOk^e}y(oC06r{^%Bxs)hb!1r& zEctP?(^3vxnC&T+#}V0`Rt@%U7_S>lU~NpI>8-j*cmgg<539md;@_sbd#y!lYp&LQ zl^Z%I%RW6%Ko?~pNjn1SYgKK}MG zjU5(f%(!8sg^HkXXQ|4Jx!9VuS=ESTv2B(GrJh99G*w|LlFcONhQ*%`YYNywt_6Ha zCD%MG7PIkwm8|l`drybTU%tN~Sc%kg8k&}XxInAedPmT>@(yBcnB7oVtZ*t*F;Y~5CmZZjMl;~4ukH8g z`Ez94cm`8bEWvIrNrpxYxY{IPWo&}33v-S3u}0L$V)imL&&->1+a0GZo&6S0QDf1Y zgD?`J?$|Q#(?f=q&4U5isEufZTXSnxtQc3jCH0Rx&%W2ZPr{D`J@yfDljczxvcKK6 zX#nJ=@8eif6QYG(^4i|dCMPbhHpwfzNLo!Nuwy=-NF3tJ?{3pE#z4hgEvQxWDN(F+ zHsLT#&U)nJr`3kh1$!g*y5Jpu7wQVC@f)OK*G#j52KP^#I>j2ZU|Uk+KWOBi&l-wm z7zybnN`xp>&{f9dUcbum$XsoIkxE)yNiy0rd#pXG`rtC>Kl+jvn60zour|AbnKHdXKB4 zDXH$$B-~azjN(|)1}xKa9SN@Rc2F%#RBrjBE&8ziHvpU|m52TO@ZDF+CNU*~iMNSt ztc&VM((NjUA=E9UNM8}Q%?K-eq+Sj68u2Vk;m=UYFy^vu_bW4W#Rr!0hjaL1;&a@e zZ9X9{DPJ8w`Z>ujOh*TUcBVTiWL_#kS&w<9(c}B6Jr)Il)esURZ1L@yqiGwj_mb~P zKY*k_6kW_SfGd+$aP_6@mNbDWCUGh40n1qsgJU9gI*DM|ZXPgG%x)WnR1bBsl%N(h z8NS?_{teI%^W&y#sMls)nZr=*);-qmSElaC-WZ)x@tMmwRJz#^iuYyCipX^}EBXeI z7UDpax(nxQcw`w{rr0+I@}y7EEIWNr!xWmZ2ktXHZ$c26X&@}5))RGTJ6xN}bN5>$ zdX+sY;M2yw(m1H!KDP}%NBjGS@HNH5w{iq;x}L|mkl?)>Qv38G2xILev+fv6IEtN> zsQ(pO1Fy5+Fsm7qPWqs*m#S}TTO{HcZU4)DzWg`9p(#In^JTI$YQ(P@qCUh>zf=G? zrjMtl2(89g`I`X3>8uq{l&aM~_c%hHk|VQ>s%@&%y1UdDW>wD+kbxGBq!~4JM4?_kh3xoy(5`*`<(_)NOSRzp^knu>->Stjw|z7?FLhKhyhq z_9RhPUcLI6@tFdsm)dufsq`wdQos+aClP(=x%%{KUd}sQyJk1`&&sFcNBA-H$+V!W&(Ivf(R3aS=Y3wrO`rgqL{-eMw!jBh5Zy>LPise(pp& zn|5n-p(}K|hJsViV@#Fhzh1WzdLvZQ?ta>l&v{}rOYt++K(vYljH_1YR#uX}92{`o zSg;~q;odM_FQklWtw$hv%pWC*6l>K!<>eQKT3E^@H&Z9ZGlDr58LDbK~_3VAJi%fRku7p+bB>a)iN> znr$j9?9DO7Fjmav6T7D|dDFz6obue_UPX>qf`(Jt4NvHEn10O(12F0(4r*d|L-13{({8@QS>P>oqj(bdXH7xspJPi>jC6bhHrvxE4;52by_H5 z8O$1b(`m?3U>fX~C5k_JLT&q4*896{=K)6S6=mQ7Tk?F=4P(!y3E#cG3|m_er?QGK z;JIq;MlYp~9sACZLvEyQs{DKDeCUFP13CQcqIj^i0<-Mt~S& z*se(iLqFc!*Q{Y1hjom_V^=v9SU-fS?+jhuqS`(wknQB79AKcM>0}ZzYP6_D{c0cv ztnu&k|1RzNJDsGS*`Begpn20?_?LYI+nC6`gXzqo?8=VP$+_5N=&id7I!}1n|uCKa!t-Qipt2 z=Jpe#e_k@}I%Pipn4KlOAR%x8{ZL)}CI1K4K#jNRL?8HsO0Ci~De_n%?xzRe=%yb!&r23*( zc63JSXT%)!M)lY=2zLMD|63dB^vb^snX~tOGU&rW%{;sIz`v_R+(VW27X;XER9Rtb z31DZwv_PTwX6^%HFgS2dT=OHqQsJ5d{;xY(uDKtu>+9=Tk1JUv`Au4N#GnIhfOAup yD^t4@aF&KzfX1{|bntnwnO2Qg6h|{>k-f$O&wstQCE%{5X+#+MRxs@M=YIifoA#vu diff --git a/doc/images/IMG_2525-MagicScaler.jpg b/doc/images/IMG_2525-MagicScaler.jpg index 07ec0d8959328e0dd0bd664fa1ff29b439f9cf0b..ecfcec02a316c031c9a51b4612f6c35d77acb262 100644 GIT binary patch delta 4284 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 iRzVC(x_!DXLZoN_2Oq;l6jkD4okw-s@*

Pyg8{1R4SW diff --git a/doc/images/IMG_2525-NetVips.jpg b/doc/images/IMG_2525-NetVips.jpg index 6a9347b414000ba0e4714cdf447466055898bd4c..b4dbb9b0bc3f4af55bfcaa0b2b8287fe0b87b6d0 100644 GIT binary patch delta 3465 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 delta 3479 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

3;yY1ACTV zl`5?|MP&q;CQhxDhyr~!Itq-cvPM;gRPaHiN~4!}o_ledA82J*Sb@JdHRJyP`6kxa zO7_O`D{E+iKFLaucr2itV;p9cDyJs`ON-`d$QYxz3Q7drdM|ISJ^T@A@+!v|bL$}_ z{kWw{r82(~g}Ih&D-ajZ0DlD+vD2ft#_45W{zxCSDpYIC2I3iSX6(hhZe_S)(gh&D zZ>2|3&~*EDkrpOk0KsQt)O_huq;*7uVm-tRWa=N8&PQQxH7f<#m6YL>^y`W@Z*Od# z(lxT%L?cz>%g;gmsZyloJ4XmrZK7yrVuLZZ7m6Qr#dUWoar`A5d}`@ZpvF&RkFWVN z`NEJ|L+?St;fk=x;MI{w;iQZmiy0VYuzS*_K+JpfdqQ9;=GbI;7A7o44sbXJik_f~ vwzvv7%_>!YL`g=!VI;^uQGVuA3I(x+RFwfnFlkbyMb!S`{C`j}e6#=AC>;@t delta 3149 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 diff --git a/doc/images/sample-ImageSharp.jpg b/doc/images/sample-ImageSharp.jpg index 12efe605bdd1a1fc302b9b6ff8ea781f6d61b409..ba4750f16825dd5fb3c76edb9ed0215ecd3de6f5 100644 GIT binary patch delta 3006 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 AL;wH) delta 2970 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 diff --git a/doc/images/sample-SkiaSharpCanvas.jpg b/doc/images/sample-SkiaSharpCanvas.jpg index 7e2b4fbf73a9a238fbfd920e87e835e5fd9df8d8..7cf851fd7acec1419ee5c84d770cd3c2b13ac680 100644 GIT binary patch delta 2929 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 diff --git a/readme.md b/readme.md index 70512d48..4de41c9f 100644 --- a/readme.md +++ b/readme.md @@ -5,20 +5,46 @@ MagicScaler High-performance image processing pipeline for .NET. Implements best-of-breed algorithms, linear light processing, and sharpening for the best image resizing quality available. Speed and efficiency are unmatched by anything else on the .NET platform. -## MagicScaler Performance +Requirements +------------ + +MagicScaler currently runs only on Windows. Although MagicScaler is compatible with -- and optimized for -- .NET Core and .NET 5+, it requires the [Windows Imaging Component](https://docs.microsoft.com/en-us/windows/desktop/wic/-wic-about-windows-imaging-codec) for its image codec support. Work is in progress to allow MagicScaler to use other native or managed codecs, which will allow running on Linux. + +Usage +----- + +Basic usage looks something like this: + +```C# +string inPath = @"c:\img\bigimage.jpg"; +string outPath = @"c:\img\smallimage.jpg"; +var settings = new ProcessImageSettings { Width = 400 }; + +using var outStream = new FileStream(outPath, FileMode.Create); +MagicImageProcessor.ProcessImage(inPath, outStream, settings); +``` + +The above example will resize `bigimage.jpg` to a width of 400 pixels and save the output to `smallimage.jpg`. The height will be set automatically to preserve the correct aspect ratio. Default settings are optimized for a balance of speed and image quality. + +The MagicScaler pipleline is also customizable if you wish to use an alternate pixel source, capture the output pixels for additional processing, or add custom filtering. + +See the [full documentation](https://docs.photosauce.net) for more details. -Benchmark results in this section come from the tests used in https://blogs.msdn.microsoft.com/dotnet/2017/01/19/net-core-image-processing/ -- updated to use current (Jan 2020) versions of the libraries and runtime. The original benchmark project [is also on GitHub](https://github.com/bleroy/core-imaging-playground). +MagicScaler Performance +----------------------- -For these results, the benchmarks were modified to use a constant `UnrollFactor` so these runs more accurately report managed memory allocations and GC counts. By default, BenchmarkDotNet runs slower benchmark methods with a smaller number of operations per iteration, meaning it can under-report allocation and GCs for those. The constant `UnrollFactor` ensures all benchmarks' reported memory stats are based on the same run counts. +Benchmark results in this section come from the tests used in https://blogs.msdn.microsoft.com/dotnet/2017/01/19/net-core-image-processing/ -- updated to use current (Apr 2021) versions of the libraries and runtime. The original benchmark project [is also on GitHub](https://github.com/bleroy/core-imaging-playground). + +*For these results, the benchmarks were modified to use a constant `UnrollFactor` so these runs more accurately report managed memory allocations and GC counts. By default, BenchmarkDotNet targets a run time in the range of 500ms-1s for each iteration. This means it executes slower benchmark methods using a smaller number of operations per iteration, and it can wildly under-report allocation and GCs, as those numbers are extrapolated from the limited iterations it runs. The constant `UnrollFactor` ensures all benchmarks' reported memory stats are based on the same run counts. The `UnrollFactor` used for each run is listed at the top of each set of results.* Benchmark environment: ``` ini -BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363 +BenchmarkDotNet=v0.12.1.1514-nightly, OS=Windows 10.0.19042.867 (20H2/October2020Update) Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores -.NET Core SDK=3.1.100 - [Host] : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT - .Net Core 3.1 CLI : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT +.NET SDK=5.0.104 + [Host] : .NET 5.0.4 (5.0.421.11614), X64 RyuJIT + .Net 5.0 CLI : .NET 5.0.4 (5.0.421.11614), X64 RyuJIT ``` ### End-to-End Image Resizing @@ -26,29 +52,29 @@ Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores First up is a semi-real-world image resizing benchmark, in which 12 JPEGs of approximately 1-megapixel each are resized to 150px wide thumbnails and saved back as JPEG. ``` ini -Job=.Net Core 3.1 CLI Toolchain=.NET Core 3.1 UnrollFactor=32 WarmupCount=5 - -| Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | -|--------------------------------------- |----------:|---------:|---------:|------:|----------:|---------:|------:|-----------:| -| *MagicScaler Load, Resize, Save(1) | 69.82 ms | 0.341 ms | 0.319 ms | 0.18 | - | - | - | 77.04 KB | -| System.Drawing Load, Resize, Save(2) | 378.48 ms | 3.185 ms | 2.980 ms | 1.00 | - | - | - | 7.71 KB | -| ImageSharp Load, Resize, Save(3) | 246.56 ms | 0.465 ms | 0.435 ms | 0.65 | 687.5000 | - | - | 3785.37 KB | -| ImageMagick Load, Resize, Save(4) | 398.36 ms | 0.797 ms | 0.745 ms | 1.05 | - | - | - | 51.03 KB | -| ImageFree Load, Resize, Save(5) | 253.21 ms | 1.184 ms | 1.108 ms | 0.67 | 6000.0000 | 656.2500 | - | 93.38 KB | -| SkiaSharp Canvas Load, Resize, Save(6) | 143.41 ms | 0.314 ms | 0.294 ms | 0.38 | 250.0000 | 62.5000 | - | 1139.36 KB | -| SkiaSharp Bitmap Load, Resize, Save(6) | 143.21 ms | 0.206 ms | 0.192 ms | 0.38 | 250.0000 | - | - | 1153.85 KB | -| NetVips Load, Resize, Save(7) | 139.27 ms | 0.819 ms | 0.766 ms | 0.37 | - | - | - | 88.99 KB | +Job=.Net 5.0 CLI Toolchain=.NET 5.0 IterationCount=5 LaunchCount=1 UnrollFactor=32 WarmupCount=5 + +| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +|--------------------------------------- |----------:|----------:|---------:|------:|--------:|----------:|----------:|----------:|----------:| +| *MagicScaler Load, Resize, Save(1) | 65.94 ms | 0.628 ms | 0.163 ms | 0.17 | 0.00 | - | - | - | 47 KB | +| System.Drawing Load, Resize, Save(2) | 377.65 ms | 1.514 ms | 0.234 ms | 1.00 | 0.00 | - | - | - | 8 KB | +| ImageSharp Load, Resize, Save(3) | 151.90 ms | 1.351 ms | 0.209 ms | 0.40 | 0.00 | 468.7500 | 31.2500 | - | 1,985 KB | +| ImageMagick Load, Resize, Save(4) | 410.55 ms | 2.867 ms | 0.444 ms | 1.09 | 0.00 | - | - | - | 50 KB | +| ImageFree Load, Resize, Save(5) | 251.34 ms | 1.129 ms | 0.175 ms | 0.67 | 0.00 | 6000.0000 | 6000.0000 | 6000.0000 | 90 KB | +| SkiaSharp Canvas Load, Resize, Save(6) | 247.03 ms | 32.118 ms | 8.341 ms | 0.66 | 0.02 | - | - | - | 95 KB | +| SkiaSharp Bitmap Load, Resize, Save(7) | 241.35 ms | 2.641 ms | 0.686 ms | 0.64 | 0.00 | - | - | - | 81 KB | +| NetVips Load, Resize, Save(8) | 133.71 ms | 2.132 ms | 0.330 ms | 0.35 | 0.00 | - | - | - | 44 KB | ``` -* (1) `PhotoSauce.MagicScaler` version 0.10.0. -* (2) `System.Drawing.Common` version 4.7.0. -* (3) `SixLabors.ImageSharp` version 1.0.0-dev003181. -* (4) `Magick.NET-Q8-AnyCPU` version 7.15.0. +* (1) `PhotoSauce.MagicScaler` version 0.12.0. +* (2) `System.Drawing.Common` version 5.0.2. +* (3) `SixLabors.ImageSharp` version 1.0.3. +* (4) `Magick.NET-Q8-AnyCPU` version 7.23.3. * (5) `FreeImage.Standard` version 4.3.8. -* (6) `SkiaSharp` version 1.68.1.1. -* (7) `NetVips` version 1.1.0 with `NetVips.Native` (libvips) version 8.8.4. +* (6) `SkiaSharp` version 2.80.2. +* (7) `NetVips` version 2.0.0 with `NetVips.Native` (libvips) version 8.10.6. -Note that unmanaged memory usage is not measured by BenchmarkDotNet, nor is managed memory allocated but never released to GC (e.g. pooled objects/buffers). See the [MagicScaler Efficiency](#magicscaler-efficiency) section for an analysis of total process memory usage for each library. +Note that unmanaged memory usage is not measured by BenchmarkDotNet's `MemoryDiagnoser`, nor is managed memory allocated but never released to GC (e.g. pooled objects/buffers). See the [MagicScaler Efficiency](#magicscaler-efficiency) section for an analysis of total process memory usage for each library. The performance numbers mostly speak for themselves, but some notes on image quality are warranted. The benchmark suite saves the output so that the visual quality of the output of each library can be compared in addition to the performance. See the [MagicScaler Quality](#magicscaler-quality) section below for details. @@ -57,96 +83,102 @@ The performance numbers mostly speak for themselves, but some notes on image qua This benchmark is the same as the previous but uses `Parallel.ForEach` to run the 12 test images in parallel. It is meant to highlight cases where the libraries' performance doesn't scale up linearly with extra processors. ``` ini -Job=.Net Core 3.1 CLI Toolchain=.NET Core 3.1 UnrollFactor=32 WarmupCount=5 - -| Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | -|----------------------------------------------- |----------:|---------:|---------:|------:|----------:|---------:|------:|-----------:| -| *MagicScaler Load, Resize, Save - Parallel | 18.85 ms | 0.283 ms | 0.265 ms | 0.12 | 31.2500 | - | - | 182.49 KB | -| System.Drawing Load, Resize, Save - Parallel | 159.21 ms | 1.519 ms | 1.421 ms | 1.00 | - | - | - | 32.74 KB | -| ImageSharp Load, Resize, Save - Parallel | 66.95 ms | 0.698 ms | 0.653 ms | 0.42 | 625.0000 | 281.2500 | - | 6652.07 KB | -| ImageMagick Load, Resize, Save - Parallel | 112.41 ms | 1.596 ms | 1.415 ms | 0.71 | - | - | - | 76.84 KB | -| ImageFree Load, Resize, Save - Parallel | 68.38 ms | 0.721 ms | 0.674 ms | 0.43 | 3625.0000 | 375.0000 | - | 117.29 KB | -| SkiaSharp Canvas Load, Resize, Save - Parallel | 38.51 ms | 0.471 ms | 0.440 ms | 0.24 | 281.2500 | 31.2500 | - | 1165.1 KB | -| SkiaSharp Bitmap Load, Resize, Save - Parallel | 38.38 ms | 0.281 ms | 0.263 ms | 0.24 | 281.2500 | 31.2500 | - | 1178.4 KB | -| NetVips Load, Resize, Save - Parallel | 67.79 ms | 0.386 ms | 0.361 ms | 0.43 | - | - | - | 116.92 KB | +Job=.Net 5.0 CLI Toolchain=.NET 5.0 IterationCount=5 LaunchCount=1 UnrollFactor=32 WarmupCount=5 + +| Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | +|----------------------------------------------- |----------:|---------:|---------:|------:|----------:|----------:|----------:|----------:| +| *MagicScaler Load, Resize, Save - Parallel | 17.83 ms | 0.857 ms | 0.133 ms | 0.11 | - | - | - | 77 KB | +| System.Drawing Load, Resize, Save - Parallel | 159.25 ms | 8.514 ms | 1.318 ms | 1.00 | - | - | - | 34 KB | +| ImageSharp Load, Resize, Save - Parallel | 41.14 ms | 1.790 ms | 0.465 ms | 0.26 | 500.0000 | 125.0000 | - | 4,573 KB | +| ImageMagick Load, Resize, Save - Parallel | 116.01 ms | 7.927 ms | 1.227 ms | 0.73 | - | - | - | 75 KB | +| ImageFree Load, Resize, Save - Parallel | 68.52 ms | 1.919 ms | 0.498 ms | 0.43 | 3875.0000 | 3875.0000 | 3875.0000 | 112 KB | +| SkiaSharp Canvas Load, Resize, Save - Parallel | 62.34 ms | 4.861 ms | 0.752 ms | 0.39 | - | - | - | 118 KB | +| SkiaSharp Bitmap Load, Resize, Save - Parallel | 62.05 ms | 4.008 ms | 1.041 ms | 0.39 | - | - | - | 104 KB | +| NetVips Load, Resize, Save - Parallel | 54.00 ms | 4.324 ms | 1.123 ms | 0.34 | - | - | - | 69 KB | ``` -Note the relative performance drop-off for NetVips. It uses multiple threads for a single operation by default, making it scale up poorly and leaving it vulnerable to [thread oversubscription](https://docs.microsoft.com/en-us/archive/blogs/visualizeparallel/oversubscription-a-classic-parallel-performance-problem) problems under heavy server load. +Note the relative performance drop-off for NetVips. It uses multiple threads for a single operation by default, making it scale up poorly and leaving it vulnerable to [CPU oversubscription](https://docs.microsoft.com/en-us/archive/blogs/visualizeparallel/oversubscription-a-classic-parallel-performance-problem) problems under heavy server load. + +Similarly, System.Drawing fails to scale up as well as the other libraries, but for the opposite reason. The System.Drawing tests run at less than 100% CPU when run in parallel, presumably due to some internal locking/serialization designed to limit memory use. + +

+Resize-Only Synthetic Benchmark ### Resize-Only Synthetic Benchmark -This benchmark creates a blank image of 1280x853 and resizes it to 150x99, throwing away the result. It is meant to separate the cost of decoding and encoding from the resizing part of the operation. It doesn't represent a real-world scenario but can be useful when looking at relative performance and overhead. +This benchmark creates a blank image of 1280x853 and resizes it to 150x99, throwing away the result. MagicScaler does very well on this one, and it's the only one MagicScaler can do on Linux (for now), but it isn't a real-world scenario, so take the results with a grain of salt. ``` ini -Job=.Net Core 3.1 CLI Toolchain=.NET Core 3.1 UnrollFactor=256 WarmupCount=5 - -| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | -|------------------------ |----------:|----------:|----------:|------:|--------:|---------:|--------:|------:|----------:| -| *MagicScaler Resize | 1.258 ms | 0.0094 ms | 0.0088 ms | 0.12 | 0.00 | - | - | - | 2208 B | -| System.Drawing Resize | 10.458 ms | 0.0954 ms | 0.0893 ms | 1.00 | 0.00 | - | - | - | 136 B | -| ImageSharp Resize | 5.701 ms | 0.0195 ms | 0.0173 ms | 0.54 | 0.00 | 31.2500 | - | - | 139440 B | -| ImageMagick Resize | 44.802 ms | 0.0829 ms | 0.0775 ms | 4.28 | 0.04 | - | - | - | 5560 B | -| FreeImage Resize | 7.525 ms | 0.0182 ms | 0.0161 ms | 0.72 | 0.01 | 500.0000 | 54.6875 | - | 136 B | -| SkiaSharp Canvas Resize | 2.032 ms | 0.0221 ms | 0.0207 ms | 0.19 | 0.00 | - | - | - | 4234 B | -| SkiaSharp Bitmap Resize | 2.006 ms | 0.0055 ms | 0.0046 ms | 0.19 | 0.00 | - | - | - | 6816 B | -| NetVips Resize | 3.768 ms | 0.0202 ms | 0.0179 ms | 0.36 | 0.00 | 3.9063 | - | - | 20921 B | +Job=.Net 5.0 CLI Toolchain=.NET 5.0 IterationCount=5 LaunchCount=1 UnrollFactor=256 WarmupCount=5 + +| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +|------------------------ |------------:|----------:|---------:|------:|--------:|---------:|---------:|---------:|----------:| +| *MagicScaler Resize | 718.4 μs | 2.76 μs | 0.43 μs | 0.07 | 0.00 | - | - | - | 1,904 B | +| System.Drawing Resize | 10,665.7 μs | 244.82 μs | 63.58 μs | 1.00 | 0.00 | - | - | - | 136 B | +| ImageSharp Resize | 2,682.1 μs | 113.49 μs | 29.47 μs | 0.25 | 0.00 | - | - | - | 9,152 B | +| ImageMagick Resize | 50,694.0 μs | 57.54 μs | 14.94 μs | 4.75 | 0.03 | - | - | - | 5,336 B | +| FreeImage Resize | 7,658.7 μs | 61.17 μs | 15.89 μs | 0.72 | 0.00 | 500.0000 | 500.0000 | 500.0000 | 136 B | +| SkiaSharp Canvas Resize | 2,500.1 μs | 259.54 μs | 67.40 μs | 0.23 | 0.01 | - | - | - | 1,584 B | +| SkiaSharp Bitmap Resize | 2,437.9 μs | 217.19 μs | 56.40 μs | 0.23 | 0.00 | - | - | - | 488 B | +| NetVips Resize | 5,703.9 μs | 278.41 μs | 72.30 μs | 0.53 | 0.01 | - | - | - | 4,152 B | ``` -Note that the NetVips benchmark case was modified to force it to use a unique image per iteration, as [suggested by the libvips author](https://github.com/bleroy/core-imaging-playground/pull/17#issuecomment-542381464). By default, Vips caches results between iterations, meaning it doesn't actually perform the resize with the benchmark code as written. - -Also note that the MagicScaler test case uses an actual test pattern rather than a blank image, so its output could be captured and evaluated. Despite the fact that it does more work, it outperforms the other libraries handily. +
-## MagicScaler Efficiency +MagicScaler Efficiency +---------------------- Raw speed isn't the only important factor when evaluating performance. As demonstrated in the parallel benchmark results above, some libraries consume extra resources in order to produce a result quickly, at the expense of overall scalability. Particularly when integrating image processing into another application, like a CMS or an E-Commerce site, it is important that your imaging library not steal resources from the rest of the system. That applies to both processor time and memory. BenchmarkDotNet does a good job of showing relative performance, and its managed memory diagnoser is quite useful for identifying excessive GC allocations, but its default configuration doesn't track actual processor usage or any memory that doesn't show up in GC collections. For example, when it reports a time of 100ms on a benchmark, was that 100ms of a single processor at 100%? More than one processor? Less than 100%? And what about memory allocated but never collected, like object caches and pooled arrays? And what about unmanaged memory? To capture these things, we must use different tools. -Because most of the libraries tested make calls to native libraries internally (ImageSharp is the only pure-managed library in the bunch), measuring only GC memory can be very misleading. And even ImageSharp's memory usage isn't accurately reflected in the BDN `MemoryDiagnoser`'s numbers, because it holds allocated heap memory in the form of pooled objects and arrays. +Because most of the libraries tested make calls to native libraries internally (ImageSharp is the only pure-managed library in the bunch), measuring only GC memory can be very misleading. And even ImageSharp's memory usage isn't accurately reflected in the BDN `MemoryDiagnoser`'s numbers, because it holds allocated heap memory in the form of pooled objects and arrays (as does MagicScaler). In order to accurately measure both CPU time and total memory usage, I devised a more real-world test. The 1-megapixel images in the benchmark test suite make for reasonable benchmark run times, but 1-megapixel is hardly representative of what we see coming from even smartphones now. In order to stress the libraries a bit more, I replaced the input images in the benchmark app's input folder with the [Bee Heads album](https://www.flickr.com/photos/usgsbiml/albums/72157633925491877) from the USGS Bee Inventory flickr. This collection contains 351 images (350 JPEG, 1 GIF), ranging in size from 2-22 megapixels, with an average of 13.4 megapixels. The total album is just over 2.5 GiB in size, and it can be downloaded directly from flickr. I re-used the image resizing code from the benchmark app but processed the test images only once, using `Parallel.ForEach` to load up the system. Because of the test image set's size, startup and JIT overhead are overshadowed by the actual image processing, and although there may be some variation in times between runs, the overall picture is accurate and is more realistic than the BDN runs that cycle through the same small set of small images. Each library's test was run in isolation so memory stats would include only that library. -This table shows the actual CPU time and peak memory usage as captured by the [Windows Performance Toolkit](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/) when running the modified benchmark app on .NET Core 3.1 x64. +This table shows the actual CPU time and peak memory usage as captured by the [Windows Performance Toolkit](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/) when running the modified benchmark app on .NET 5.0 x64. | Method | Peak Memory | VirtualAlloc Total | CPU Time | |------------------------- |------------:|-------------------:|----------:| -| *MagicScaler Bee Heads | 373 MB | 4752 MB | 55701 ms | -| System.Drawing Bee Heads | 1082 MB | 36553 MB | 194308 ms | -| ImageSharp Bee Heads | 8719 MB | 27920 MB | 195293 ms | -| ImageMagick Bee Heads | 771 MB | 17394 MB | 288371 ms | -| FreeImage Bee Heads | 670 MB | 16668 MB | 220011 ms | -| SkiaSharp Bee Heads | 911 MB | 26450 MB | 135316 ms | -| NetVips Bee Heads | 3278 MB | 3428 MB | 88533 ms | +| *MagicScaler Bee Heads | 404 MB | 4749 MB | 50053 ms | +| System.Drawing Bee Heads | 849 MB | 36435 MB | 201269 ms | +| ImageSharp Bee Heads | 7459 MB | 27575 MB | 161416 ms | +| ImageMagick Bee Heads | 699 MB | 17481 MB | 291919 ms | +| FreeImage Bee Heads | 612 MB | 16408 MB | 232971 ms | +| SkiaSharp Bee Heads | 991 MB | 26368 MB | 190533 ms | +| NetVips Bee Heads | 443 MB | 3170 MB | 130149 ms | A few of the more interesting points from the above numbers: ImageSharp shows relatively low allocations and GC counts in the BDN managed memory diagnoser, but it is clear from the WPT trace that it is allocating and never releasing large amounts of memory. In a repetitive benchmark, this might not be obvious, but when working on a larger number of larger images, it will put a major strain on memory. -Similarly, Vips uses huge amounts of memory without releasing it. The ratio of peak memory to total allocated memory indicates that it is doing aggressive caching. This also would not show up as well in a repetitive benchmark, but it could negatively impact anything sharing memory space with it. +It's clear from the CPU time numbers that System.Drawing is spending a fair amount of its time idle. Its total consumed time is roughly the same as SkiaSharp's, but its wall clock time shows it to be roughly 3x slower. This can be seen in WPA's CPU utilization graphs but not in the BDN results. -It's clear from the CPU time numbers that System.Drawing is spending a fair amount of its time idle. Its total consumed time is roughly the same as ImageSharp's, but its wall clock time shows it to be roughly 4x slower. +NetVips shows higher CPU time in the WPT trace than it does running without tracing. Its wall-clock time was almost identical to MagicScaler's on this image set. Both Vips and MagicScaler lazily evaluate requests for pixels, so their memory usage is significantly lower than other libraries'. -And it's clear that MagicScaler is the most efficient by far in terms of both memory and CPU time. +
+32-bit Process Results +
-Because the peak memory numbers for some libraries are so high, it's also worth looking at how they perform in 32-bit environment, which is naturally memory-constrained. The following table shows results of the same test for .NET Core 3.1 x86. +Because the peak memory numbers for some libraries are so high, it's also worth looking at how they perform in 32-bit environment, which is naturally memory constrained. The following table shows results of the same test for .NET Core 5.0 x86. | Method | Peak Memory | VirtualAlloc Total | CPU Time | |------------------------- |------------:|-------------------:|----------:| -| *MagicScaler Bee Heads | 409 MB | 4746 MB | 63937 ms | -| System.Drawing Bee Heads | 969 MB | 36503 MB | 209003 ms | +| *MagicScaler Bee Heads | 361 MB | 4747 MB | 57250 ms | +| System.Drawing Bee Heads | 769 MB | 36405 MB | 219007 ms | | ImageSharp Bee Heads | FAIL - OOM | | | -| ImageMagick Bee Heads | 721 MB | 17342 MB | 367823 ms | -| FreeImage Bee Heads | 667 MB | 16685 MB | 250278 ms | -| SkiaSharp Bee Heads | 941 MB | 26418 MB | 155130 ms | -| NetVips Bee Heads | FAIL - OOM | | | +| ImageMagick Bee Heads | 701 MB | 17381 MB | 359497 ms | +| FreeImage Bee Heads | 671 MB | 16393 MB | 264883 ms | +| SkiaSharp Bee Heads | 944 MB | 26366 MB | 288483 ms | +| NetVips Bee Heads | 424 MB | 3277 MB | 94891 ms | -As one might expect, ImageSharp and NetVips both failed to complete the test on 32-bit .NET Core. ImageSharp failed almost immediately, while NetVips made it just over halfway through the test. +ImageSharp failed to complete the test with less addressable memory available. -Once again, MagicScaler is the most efficient with memory and CPU. All libraries that succeeded took longer in 32-bit mode, but ImageMagick was disproportionately slower. +Once again, MagicScaler is the most efficient with memory and CPU. However once again, NetVips completed the suite in similar wall-clock time to MagicScaler, so its CPU time may not be accurate under the profiler. All libraries that succeeded took longer in 32-bit mode, but SkiaSharp was disproportionately slower. -For the record, here are the exceptions thrown by the failing libraries: +For the record, here is the exception thrown by ImageSharp: ImageSharp ``` @@ -156,17 +188,10 @@ System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was ... ``` -NetVips -``` -NetVips.VipsException: unable to call jpegsave -VipsJpeg: Insufficient memory (case 4) - - at NetVips.Operation.Call(String operationName, VOption kwargs, Image matchImage, Object[] args) - at NetVips.Image.Jpegsave(String filename, Nullable`1 pageHeight, Nullable`1 q, String profile, Nullable`1 optimizeCoding, Nullable`1 interlace, Nullable`1 noSubsample, Nullable`1 trellisQuant, Nullable`1 overshootDeringing, Nullable`1 optimizeScans, Nullable`1 quantTable, Nullable`1 strip, Double[] background) - ... -``` +
-## MagicScaler Quality +MagicScaler Quality +------------------- The benchmark application detailed above saves the output from its end-to-end image resizing tests so they can be evaluated for file size and image quality. The images were contributed by @bleroy, who created the original benchmark. They were chosen because of their high-frequency detail which had been observed to be challenging for some image resampling algorithms. The images have a couple of other interesting properties as well. @@ -178,7 +203,15 @@ Note that outside the ill-advised overrides to JPEG quality settings, the result ### Color Management -Handling images in a color space other than sRGB can be a challenge, and it's not something most developers are familiar with. There are essentially 3 ways to approach an image with an embedded color profile: +Handling images in a color space other than sRGB can be a challenge, and it's not something most developers are familiar with. Now that all iOS and most Android devices' camera apps default to the [Display P3](https://en.wikipedia.org/wiki/DCI-P3#Display_P3) color space and wide-gamut displays are common, this is a topic of increasing importance. + +MagicScaler will automatically normalize the color space of images it processes to maximize compatibility with image consumers while preserving the original gamut of the image. Other software may not be color aware or may make it difficult to process in a color-correct manner. + +
+Additional Info on Color Space Handling +
+ +There are essentially 3 ways to approach an image with an embedded color profile: 1) Convert the image to sRGB. This has the advantage that any downstream software that reads the processed image does not need to be color managed to display it correctly. This was historically a problem with many apps, including popular web browsers (you can [test yours here](https://chromachecker.com/webbrowser/en/manual)). The disadvantage is that it takes extra processing to do this conversion. 2) Preserve the color space by embedding the ICC profile in the output image. This is basically the opposite of option 1). It's cheaper to do but may result in other software mangling the colors later. It also results in a larger file because the profile may be very large -- in the case of a thumbnail, it might double the file size or more. @@ -190,15 +223,17 @@ The libraries tested in the benchmark have different capabilities, so the option * ImageSharp can do 2) or 3) and does 2) by default. When it was originally integrated into the benchmark, however, it only did 3). * ImageMagick can do any of the options above but does 2) by default. However, it also preserves all other metadata by default, and the test images have quite a lot of metadata. This results in thumbnails that are extremely oversized and consist of roughly 90% metadata. For this reason, the ImageMagick tests were written to strip all metadata, resulting in behavior 3). * FreeImage works the same way as ImageMagick by default, and its test was implemented the same way. It could have done option 2), but it was implemented to do option 3). -* Skia does option 3) by default but can be made to do option 1) with a lot of [extra code](https://skia.org/user/sample/color?cl=9919). This was not done in the benchmark tests. -* MagicScaler can do any of the above options but does option 1) by default. I contributed the MagicScaler test in the benchmark myself. It has been correct from the beginning. +* Skia can be made to do option 1) with a lot of [extra code](https://skia.org/user/sample/color?cl=9919). This was not done in the benchmark tests, resulting in behavior 3). Update: as of SkiaSharp 2.80, behavior 1) is now default, but with a marked reduction in speed. +* MagicScaler can do any of the above options but does option 2) by default. I contributed the MagicScaler test in the benchmark myself, and it has been correct from the beginning, although it used behavior 1) by default initially. -The net result is that if you look at the sample image output in @bleroy's [blog post](https://devblogs.microsoft.com/dotnet/net-core-image-processing/), the MagicScaler output has different colors than all the others. 10 of the 12 images have washed-out colors in the output from the other libraries -- most apparent in the vibrant red, green, and blue hues, such as the snake or the Wild River ride on the back of what is now the [MoPOP building](https://www.mopop.org/building). If you download the project today and run it, the outputs from System.Drawing (corrected by me), ImageSharp (fixed in the library), and MagicScaler will all have correct colors, and the rest will be wrong. +The net result is that if you look at the sample image output in @bleroy's [blog post](https://devblogs.microsoft.com/dotnet/net-core-image-processing/), the MagicScaler output has different colors than all the others. 10 of the 12 images have washed-out colors in the output from the other libraries -- most apparent in the vibrant red, green, and blue hues, such as the snake or the Wild River ride on the back of what is now the [MoPOP building](https://www.mopop.org/building). If you download the project today and run it, the outputs from System.Drawing (corrected by me), ImageSharp (fixed in the library), SkiaSharp (fixed in the library), and MagicScaler will all have correct colors, and the rest will be wrong. The recently-added NetVips test has the same problem as ImageMagick. It would do option 2) by default but would carry all the metadata with it and so has been written to do 3). Like ImageMagick, it could be modified to do option 1) instead, but that would require much more code and would be significantly slower. These are common mistakes made by developers starting out with image processing, because it can be easy to miss the shift in colors and difficult to discover how to do the right thing. +
+ Sample Images: @@ -258,32 +293,6 @@ The linear light blending combined with the sharpening work to preserve more det Also of note is that ImageSharp's thumbnail for this image is roughly twice the size of the others because it has embedded the 3KiB sRGB color profile from the original image unnecessarily. -Requirements ------------- - -MagicScaler currently runs only on Windows. Although MagicScaler is compatible with (and optimized for) .NET Core, it requires the [Windows Imaging Component](https://docs.microsoft.com/en-us/windows/desktop/wic/-wic-about-windows-imaging-codec) to function. Work is in progress to allow MagicScaler to run on other platforms. - -Usage ------ - -Basic usage looks something like this: - -```C# -string inPath = @"c:\img\bigimage.jpg"; -string outPath = @"c:\img\smallimage.jpg"; -var settings = new ProcessImageSettings { Width = 400 }; - -using var outStream = new FileStream(outPath, FileMode.Create); -MagicImageProcessor.ProcessImage(inPath, outStream, settings); -``` - -The above example will resize `bigimage.jpg` to a width of 400 pixels and save the output to `smallimage.jpg`. The height will be set automatically to preserve the correct aspect ratio. Default settings are optimized for a balance of speed and image quality. - -The MagicScaler pipleline is also customizable if you wish to use an alternate pixel source, capture the output pixels for additional processing, or add custom filtering. - -See the [full documentation](https://docs.photosauce.net) for more details. - - WebRSize ======== @@ -319,7 +328,7 @@ This project is using [semantic versioning](http://semver.org/). Releases witho Contributing ------------ -Contributions are welcome, but please open a new issue for discussion before submitting any significant pull requests. This will hopefully save any wasted or duplicate efforts. +Contributions are welcome, but please open a new issue or discussion before submitting any pull requests that alter API or functionality. This will hopefully save any wasted or duplicate efforts. License ------- diff --git a/src/MagicScaler/Magic/OctreeQuantizer.cs b/src/MagicScaler/Magic/OctreeQuantizer.cs index e7fb5e20..109b1309 100644 --- a/src/MagicScaler/Magic/OctreeQuantizer.cs +++ b/src/MagicScaler/Magic/OctreeQuantizer.cs @@ -1112,6 +1112,10 @@ private static void initFreeList(Span listBuff) Unsafe.InitBlockUnaligned(ref listPtr, 0, reserveNodes * sizeof(ushort)); } + // TODO Check/log RyuJIT issue. All the static methods on the node structs should be instance methods or simply direct + // accesses to the fixed fields, but the JIT currently inserts extraneous null checks on pointer deref when inlining them. + // For the same reason, the fields are not used in the methods; we cast the node to the field type and offset manually. + // Related: https://github.com/dotnet/runtime/issues/37727 [StructLayout(LayoutKind.Explicit)] private struct HistogramNode { @@ -1128,8 +1132,6 @@ private struct HistogramNode public static ReadOnlySpan SumsMask => new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f }; - // TODO Check/log RyuJIT issue. All the following static methods should be instance methods - // but the JIT currently inserts extraneous null checks on pointer deref when inlining them. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void AddSample(HistogramNode* node, uint bgr) { @@ -1213,7 +1215,7 @@ private struct PaletteMapNode [FieldOffset(0)] public fixed byte PaletteIndices[8]; [FieldOffset(12)] - public int LeafValue; + public uint LeafValue; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool HasChildren(PaletteMapNode* node) diff --git a/src/WebRSize/WebRSize.csproj b/src/WebRSize/WebRSize.csproj index e6525908..6b2d7f73 100644 --- a/src/WebRSize/WebRSize.csproj +++ b/src/WebRSize/WebRSize.csproj @@ -1,7 +1,7 @@ - 0.6.2 + 0.6.3 net472 $(TargetFrameworks);net461