123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197(*
* Copyright 2011 The Savonet Team
*
* This file is part of ocaml-mm.
*
* ocaml-mm is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* ocaml-mm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ocaml-mm; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* As a special exception to the GNU Library General Public License, you may
* link, statically or dynamically, a "work that uses the Library" with a publicly
* distributed version of the Library to produce an executable file containing
* portions of the Library, and distribute that executable file under terms of
* your choice, without any of the additional requirements listed in clause 6
* of the GNU Library General Public License.
* By "a publicly distributed version of the Library", we mean either the unmodified
* Library as distributed by The Savonet Team, or a modified version of the Library that is
* distributed under the conditions defined in clause 3 of the GNU Library General
* Public License. This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU Library General Public License.
*
*)openImageBasemoduletypeCanvasImage=sigtypetvalwidth:t->intvalheight:t->intvalsize:t->intvalcreate:int->int->tvalblank:t->unitvalcopy:t->tvaladd:t->?x:int->?y:int->t->unitvalhas_alpha:t->boolvalfill_alpha:t->int->unitvalset_pixel_rgba:t->int->int->Pixel.rgba->unitvalrandomize:t->unitvalscale:t->t->unitend(** A canvas of images. The structure is immutable but its elements might be
returned and therefore should not be used in place. *)moduleCanvas(I:CanvasImage)=structmoduleElement=structtypet=Imageof(int*int)*I.t(** An image at given offset. *)letsize=functionImage(_,img)->I.sizeimglettranslatedxdy=function|Image((x,y),img)->Image((x+dx,y+dy),img)endmoduleE=Elementtypet={width:int;height:int;elements:E.tlist}letcreatewidthheight={width;height;elements=[]}letwidthc=c.widthletheightc=c.heightletsizec=List.fold_left(funne->n+E.sizee)0c.elementsletmake?width?height?(x=0)?(y=0)image=letwidth=Option.value~default:(I.widthimage)widthinletheight=Option.value~default:(I.heightimage)heightin{width;height;elements=[E.Image((x,y),image)]}letaddcc'=(* assert ((c.width < 0 || c.width = c'.width) && (c.height < 0 || c.height = c'.height)); *){width=c'.width;height=c'.height;elements=c.elements@c'.elements;}(* TODO: improve precision with something like this:
https://stackoverflow.com/questions/2628118/rectangles-covering *)letcoveringc=letwidth=widthcinletheight=heightcinletcovering_element=function|E.Image((x,y),img)->letw=I.widthimginleth=I.heightimginx<=0&&y<=0&&x+w>=width&&y+h>=height&¬(I.has_alphaimg)inList.existscovering_elementc.elementsletrender?(fresh=false)?(transparent=true)c=assert(widthc>=0&&heightc>=0);matchc.elementswith|[Image((0,0),img)]when(notfresh)&&I.widthimg=widthc&&I.heightimg=heightc->img|elements->letr=I.create(widthc)(heightc)inifnot(coveringc)then(I.blankr;iftransparentthenI.fill_alphar0);letadd=functionE.Image((x,y),img)->I.addimg~x~yrinList.iter_rightaddelements;rletrendered?transparentc=make(render?transparentc)letmapfc=make(f(renderc))letiterfc=letimg=render~fresh:truecinfimg;makeimglettranslatedxdyc=ifdx=0&&dy=0thencelse{cwithelements=List.map(E.translatedxdy)c.elements}letviewport?(x=0)?(y=0)widthheightc=translate(-x)(-y){cwithwidth;height}letbounding_boxc=letp=(widthc,heightc)inletd=(0,0)inList.fold_left(fun(p,d)->function|E.Image((x,y),img)->(Point.minp(x,y),Point.maxd(I.widthimg,I.heightimg)))(p,d)c.elementsletscale?(scaler=I.scale)(nx,dx)(ny,dy)c=ifnx=dx&&ny=dythencelse(letelements=List.map(function|E.Image((x,y),img)->letscl=I.create(I.widthimg*nx/dx)(I.heightimg*ny/dy)inscalerimgscl;E.Image((x*nx/dx,y*ny/dy),scl))c.elementsin{width=c.width;height=c.height;elements})letresize?(proportional=true)?scalerw'h'img=letw=widthimginleth=heightimginlet(nx,dx),(ny,dy)=ifproportionalthen(letf=Fraction.min(w',w)(h',h)in(f,f))else((w',w),(h',h))inletx,y=ifproportionalthen(0,0)else((w'-(w*nx/dx))/2,(h'-(h*ny/dy))/2)inscale?scaler(nx,dx)(ny,dy)img|>translatexy|>viewportw'h'moduleDraw=structletlinecolor(x1,y1)(x2,y2)=letdx=minx1x2inletdy=miny1y2inletw=abs(x2-x1)inleth=abs(y2-y1)inletbuf=I.createwhinI.blankbuf;I.fill_alphabuf0;Draw.line(funij->if0<=i&&i<w&&0<=j&&j<hthenI.set_pixel_rgbabufijcolor)(x1-dx,y1-dy)(x2-dx,y2-dy);make~x:dx~y:dy~width:(-1)~height:(-1)bufendendmoduleCanvasYUV420=Canvas(structincludeImageYUV420letcreatewh=createwhletscale=scale~proportional:falseend)