CanvasRenderingContext2d.cc 101 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361
  1. // Copyright (c) 2010 LearnBoost <tj@learnboost.com>
  2. #include "CanvasRenderingContext2d.h"
  3. #include <algorithm>
  4. #include "backend/ImageBackend.h"
  5. #include <cairo-pdf.h>
  6. #include "Canvas.h"
  7. #include "CanvasGradient.h"
  8. #include "CanvasPattern.h"
  9. #include <cmath>
  10. #include <cstdlib>
  11. #include "Image.h"
  12. #include "ImageData.h"
  13. #include <limits>
  14. #include <map>
  15. #include "Point.h"
  16. #include <string>
  17. #include "Util.h"
  18. #include <vector>
  19. using namespace v8;
  20. Nan::Persistent<FunctionTemplate> Context2d::constructor;
  21. /*
  22. * Rectangle arg assertions.
  23. */
  24. #define RECT_ARGS \
  25. double args[4]; \
  26. if(!checkArgs(info, args, 4)) \
  27. return; \
  28. double x = args[0]; \
  29. double y = args[1]; \
  30. double width = args[2]; \
  31. double height = args[3];
  32. #define CHECK_RECEIVER(prop) \
  33. if (!Context2d::constructor.Get(info.GetIsolate())->HasInstance(info.This())) { \
  34. Nan::ThrowTypeError("Method " #prop " called on incompatible receiver"); \
  35. return; \
  36. }
  37. constexpr double twoPi = M_PI * 2.;
  38. /*
  39. * Simple helper macro for a rather verbose function call.
  40. */
  41. #define PANGO_LAYOUT_GET_METRICS(LAYOUT) pango_context_get_metrics( \
  42. pango_layout_get_context(LAYOUT), \
  43. pango_layout_get_font_description(LAYOUT), \
  44. pango_context_get_language(pango_layout_get_context(LAYOUT)))
  45. inline static bool checkArgs(const Nan::FunctionCallbackInfo<Value> &info, double *args, int argsNum, int offset = 0){
  46. int argsEnd = offset + argsNum;
  47. bool areArgsValid = true;
  48. for (int i = offset; i < argsEnd; i++) {
  49. double val = Nan::To<double>(info[i]).FromMaybe(0);
  50. if (areArgsValid) {
  51. if (!std::isfinite(val)) {
  52. // We should continue the loop instead of returning immediately
  53. // See https://html.spec.whatwg.org/multipage/canvas.html
  54. areArgsValid = false;
  55. continue;
  56. }
  57. args[i - offset] = val;
  58. }
  59. }
  60. return areArgsValid;
  61. }
  62. Nan::Persistent<Function> Context2d::_DOMMatrix;
  63. Nan::Persistent<Function> Context2d::_parseFont;
  64. /*
  65. * Initialize Context2d.
  66. */
  67. void
  68. Context2d::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
  69. Nan::HandleScope scope;
  70. // Constructor
  71. Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(Context2d::New);
  72. constructor.Reset(ctor);
  73. ctor->InstanceTemplate()->SetInternalFieldCount(1);
  74. ctor->SetClassName(Nan::New("CanvasRenderingContext2D").ToLocalChecked());
  75. // Prototype
  76. Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
  77. Nan::SetPrototypeMethod(ctor, "drawImage", DrawImage);
  78. Nan::SetPrototypeMethod(ctor, "putImageData", PutImageData);
  79. Nan::SetPrototypeMethod(ctor, "getImageData", GetImageData);
  80. Nan::SetPrototypeMethod(ctor, "createImageData", CreateImageData);
  81. Nan::SetPrototypeMethod(ctor, "addPage", AddPage);
  82. Nan::SetPrototypeMethod(ctor, "save", Save);
  83. Nan::SetPrototypeMethod(ctor, "restore", Restore);
  84. Nan::SetPrototypeMethod(ctor, "rotate", Rotate);
  85. Nan::SetPrototypeMethod(ctor, "translate", Translate);
  86. Nan::SetPrototypeMethod(ctor, "transform", Transform);
  87. Nan::SetPrototypeMethod(ctor, "getTransform", GetTransform);
  88. Nan::SetPrototypeMethod(ctor, "resetTransform", ResetTransform);
  89. Nan::SetPrototypeMethod(ctor, "setTransform", SetTransform);
  90. Nan::SetPrototypeMethod(ctor, "isPointInPath", IsPointInPath);
  91. Nan::SetPrototypeMethod(ctor, "scale", Scale);
  92. Nan::SetPrototypeMethod(ctor, "clip", Clip);
  93. Nan::SetPrototypeMethod(ctor, "fill", Fill);
  94. Nan::SetPrototypeMethod(ctor, "stroke", Stroke);
  95. Nan::SetPrototypeMethod(ctor, "fillText", FillText);
  96. Nan::SetPrototypeMethod(ctor, "strokeText", StrokeText);
  97. Nan::SetPrototypeMethod(ctor, "fillRect", FillRect);
  98. Nan::SetPrototypeMethod(ctor, "strokeRect", StrokeRect);
  99. Nan::SetPrototypeMethod(ctor, "clearRect", ClearRect);
  100. Nan::SetPrototypeMethod(ctor, "rect", Rect);
  101. Nan::SetPrototypeMethod(ctor, "roundRect", RoundRect);
  102. Nan::SetPrototypeMethod(ctor, "measureText", MeasureText);
  103. Nan::SetPrototypeMethod(ctor, "moveTo", MoveTo);
  104. Nan::SetPrototypeMethod(ctor, "lineTo", LineTo);
  105. Nan::SetPrototypeMethod(ctor, "bezierCurveTo", BezierCurveTo);
  106. Nan::SetPrototypeMethod(ctor, "quadraticCurveTo", QuadraticCurveTo);
  107. Nan::SetPrototypeMethod(ctor, "beginPath", BeginPath);
  108. Nan::SetPrototypeMethod(ctor, "closePath", ClosePath);
  109. Nan::SetPrototypeMethod(ctor, "arc", Arc);
  110. Nan::SetPrototypeMethod(ctor, "arcTo", ArcTo);
  111. Nan::SetPrototypeMethod(ctor, "ellipse", Ellipse);
  112. Nan::SetPrototypeMethod(ctor, "setLineDash", SetLineDash);
  113. Nan::SetPrototypeMethod(ctor, "getLineDash", GetLineDash);
  114. Nan::SetPrototypeMethod(ctor, "createPattern", CreatePattern);
  115. Nan::SetPrototypeMethod(ctor, "createLinearGradient", CreateLinearGradient);
  116. Nan::SetPrototypeMethod(ctor, "createRadialGradient", CreateRadialGradient);
  117. Nan::SetAccessor(proto, Nan::New("pixelFormat").ToLocalChecked(), GetFormat);
  118. Nan::SetAccessor(proto, Nan::New("patternQuality").ToLocalChecked(), GetPatternQuality, SetPatternQuality);
  119. Nan::SetAccessor(proto, Nan::New("imageSmoothingEnabled").ToLocalChecked(), GetImageSmoothingEnabled, SetImageSmoothingEnabled);
  120. Nan::SetAccessor(proto, Nan::New("globalCompositeOperation").ToLocalChecked(), GetGlobalCompositeOperation, SetGlobalCompositeOperation);
  121. Nan::SetAccessor(proto, Nan::New("globalAlpha").ToLocalChecked(), GetGlobalAlpha, SetGlobalAlpha);
  122. Nan::SetAccessor(proto, Nan::New("shadowColor").ToLocalChecked(), GetShadowColor, SetShadowColor);
  123. Nan::SetAccessor(proto, Nan::New("miterLimit").ToLocalChecked(), GetMiterLimit, SetMiterLimit);
  124. Nan::SetAccessor(proto, Nan::New("lineWidth").ToLocalChecked(), GetLineWidth, SetLineWidth);
  125. Nan::SetAccessor(proto, Nan::New("lineCap").ToLocalChecked(), GetLineCap, SetLineCap);
  126. Nan::SetAccessor(proto, Nan::New("lineJoin").ToLocalChecked(), GetLineJoin, SetLineJoin);
  127. Nan::SetAccessor(proto, Nan::New("lineDashOffset").ToLocalChecked(), GetLineDashOffset, SetLineDashOffset);
  128. Nan::SetAccessor(proto, Nan::New("shadowOffsetX").ToLocalChecked(), GetShadowOffsetX, SetShadowOffsetX);
  129. Nan::SetAccessor(proto, Nan::New("shadowOffsetY").ToLocalChecked(), GetShadowOffsetY, SetShadowOffsetY);
  130. Nan::SetAccessor(proto, Nan::New("shadowBlur").ToLocalChecked(), GetShadowBlur, SetShadowBlur);
  131. Nan::SetAccessor(proto, Nan::New("antialias").ToLocalChecked(), GetAntiAlias, SetAntiAlias);
  132. Nan::SetAccessor(proto, Nan::New("textDrawingMode").ToLocalChecked(), GetTextDrawingMode, SetTextDrawingMode);
  133. Nan::SetAccessor(proto, Nan::New("quality").ToLocalChecked(), GetQuality, SetQuality);
  134. Nan::SetAccessor(proto, Nan::New("currentTransform").ToLocalChecked(), GetCurrentTransform, SetCurrentTransform);
  135. Nan::SetAccessor(proto, Nan::New("fillStyle").ToLocalChecked(), GetFillStyle, SetFillStyle);
  136. Nan::SetAccessor(proto, Nan::New("strokeStyle").ToLocalChecked(), GetStrokeStyle, SetStrokeStyle);
  137. Nan::SetAccessor(proto, Nan::New("font").ToLocalChecked(), GetFont, SetFont);
  138. Nan::SetAccessor(proto, Nan::New("textBaseline").ToLocalChecked(), GetTextBaseline, SetTextBaseline);
  139. Nan::SetAccessor(proto, Nan::New("textAlign").ToLocalChecked(), GetTextAlign, SetTextAlign);
  140. Local<Context> ctx = Nan::GetCurrentContext();
  141. Nan::Set(target, Nan::New("CanvasRenderingContext2d").ToLocalChecked(), ctor->GetFunction(ctx).ToLocalChecked());
  142. Nan::Set(target, Nan::New("CanvasRenderingContext2dInit").ToLocalChecked(), Nan::New<Function>(SaveExternalModules));
  143. }
  144. /*
  145. * Create a cairo context.
  146. */
  147. Context2d::Context2d(Canvas *canvas) {
  148. _canvas = canvas;
  149. _context = canvas->createCairoContext();
  150. _layout = pango_cairo_create_layout(_context);
  151. states.emplace();
  152. state = &states.top();
  153. pango_layout_set_font_description(_layout, state->fontDescription);
  154. }
  155. /*
  156. * Destroy cairo context.
  157. */
  158. Context2d::~Context2d() {
  159. g_object_unref(_layout);
  160. cairo_destroy(_context);
  161. _resetPersistentHandles();
  162. }
  163. /*
  164. * Reset canvas state.
  165. */
  166. void Context2d::resetState() {
  167. states.pop();
  168. states.emplace();
  169. pango_layout_set_font_description(_layout, state->fontDescription);
  170. _resetPersistentHandles();
  171. }
  172. void Context2d::_resetPersistentHandles() {
  173. _fillStyle.Reset();
  174. _strokeStyle.Reset();
  175. _font.Reset();
  176. }
  177. /*
  178. * Save cairo / canvas state.
  179. */
  180. void
  181. Context2d::save() {
  182. cairo_save(_context);
  183. states.emplace(states.top());
  184. state = &states.top();
  185. }
  186. /*
  187. * Restore cairo / canvas state.
  188. */
  189. void
  190. Context2d::restore() {
  191. if (states.size() > 1) {
  192. cairo_restore(_context);
  193. states.pop();
  194. state = &states.top();
  195. pango_layout_set_font_description(_layout, state->fontDescription);
  196. }
  197. }
  198. /*
  199. * Save flat path.
  200. */
  201. void
  202. Context2d::savePath() {
  203. _path = cairo_copy_path_flat(_context);
  204. cairo_new_path(_context);
  205. }
  206. /*
  207. * Restore flat path.
  208. */
  209. void
  210. Context2d::restorePath() {
  211. cairo_new_path(_context);
  212. cairo_append_path(_context, _path);
  213. cairo_path_destroy(_path);
  214. }
  215. /*
  216. * Create temporary surface for gradient or pattern transparency
  217. */
  218. cairo_pattern_t*
  219. create_transparent_gradient(cairo_pattern_t *source, float alpha) {
  220. double x0;
  221. double y0;
  222. double x1;
  223. double y1;
  224. double r0;
  225. double r1;
  226. int count;
  227. int i;
  228. double offset;
  229. double r;
  230. double g;
  231. double b;
  232. double a;
  233. cairo_pattern_t *newGradient;
  234. cairo_pattern_type_t type = cairo_pattern_get_type(source);
  235. cairo_pattern_get_color_stop_count(source, &count);
  236. if (type == CAIRO_PATTERN_TYPE_LINEAR) {
  237. cairo_pattern_get_linear_points (source, &x0, &y0, &x1, &y1);
  238. newGradient = cairo_pattern_create_linear(x0, y0, x1, y1);
  239. } else if (type == CAIRO_PATTERN_TYPE_RADIAL) {
  240. cairo_pattern_get_radial_circles(source, &x0, &y0, &r0, &x1, &y1, &r1);
  241. newGradient = cairo_pattern_create_radial(x0, y0, r0, x1, y1, r1);
  242. } else {
  243. Nan::ThrowError("Unexpected gradient type");
  244. return NULL;
  245. }
  246. for ( i = 0; i < count; i++ ) {
  247. cairo_pattern_get_color_stop_rgba(source, i, &offset, &r, &g, &b, &a);
  248. cairo_pattern_add_color_stop_rgba(newGradient, offset, r, g, b, a * alpha);
  249. }
  250. return newGradient;
  251. }
  252. cairo_pattern_t*
  253. create_transparent_pattern(cairo_pattern_t *source, float alpha) {
  254. cairo_surface_t *surface;
  255. cairo_pattern_get_surface(source, &surface);
  256. int width = cairo_image_surface_get_width(surface);
  257. int height = cairo_image_surface_get_height(surface);
  258. cairo_surface_t *mask_surface = cairo_image_surface_create(
  259. CAIRO_FORMAT_ARGB32,
  260. width,
  261. height);
  262. cairo_t *mask_context = cairo_create(mask_surface);
  263. if (cairo_status(mask_context) != CAIRO_STATUS_SUCCESS) {
  264. Nan::ThrowError("Failed to initialize context");
  265. return NULL;
  266. }
  267. cairo_set_source(mask_context, source);
  268. cairo_paint_with_alpha(mask_context, alpha);
  269. cairo_destroy(mask_context);
  270. cairo_pattern_t* newPattern = cairo_pattern_create_for_surface(mask_surface);
  271. cairo_surface_destroy(mask_surface);
  272. return newPattern;
  273. }
  274. /*
  275. * Fill and apply shadow.
  276. */
  277. void
  278. Context2d::setFillRule(v8::Local<v8::Value> value) {
  279. cairo_fill_rule_t rule = CAIRO_FILL_RULE_WINDING;
  280. if (value->IsString()) {
  281. Nan::Utf8String str(value);
  282. if (std::strcmp(*str, "evenodd") == 0) {
  283. rule = CAIRO_FILL_RULE_EVEN_ODD;
  284. }
  285. }
  286. cairo_set_fill_rule(_context, rule);
  287. }
  288. void
  289. Context2d::fill(bool preserve) {
  290. cairo_pattern_t *new_pattern;
  291. bool needsRestore = false;
  292. if (state->fillPattern) {
  293. if (state->globalAlpha < 1) {
  294. new_pattern = create_transparent_pattern(state->fillPattern, state->globalAlpha);
  295. if (new_pattern == NULL) {
  296. // failed to allocate; Nan::ThrowError has already been called, so return from this fn.
  297. return;
  298. }
  299. cairo_set_source(_context, new_pattern);
  300. cairo_pattern_destroy(new_pattern);
  301. } else {
  302. cairo_pattern_set_filter(state->fillPattern, state->patternQuality);
  303. cairo_set_source(_context, state->fillPattern);
  304. }
  305. repeat_type_t repeat = Pattern::get_repeat_type_for_cairo_pattern(state->fillPattern);
  306. if (repeat == NO_REPEAT) {
  307. cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_NONE);
  308. } else if (repeat == REPEAT) {
  309. cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_REPEAT);
  310. } else {
  311. cairo_save(_context);
  312. cairo_path_t *savedPath = cairo_copy_path(_context);
  313. cairo_surface_t *patternSurface = nullptr;
  314. cairo_pattern_get_surface(cairo_get_source(_context), &patternSurface);
  315. double width, height;
  316. if (repeat == REPEAT_X) {
  317. double x1, x2;
  318. cairo_path_extents(_context, &x1, nullptr, &x2, nullptr);
  319. width = x2 - x1;
  320. height = cairo_image_surface_get_height(patternSurface);
  321. } else {
  322. double y1, y2;
  323. cairo_path_extents(_context, nullptr, &y1, nullptr, &y2);
  324. width = cairo_image_surface_get_width(patternSurface);
  325. height = y2 - y1;
  326. }
  327. cairo_new_path(_context);
  328. cairo_rectangle(_context, 0, 0, width, height);
  329. cairo_clip(_context);
  330. cairo_append_path(_context, savedPath);
  331. cairo_path_destroy(savedPath);
  332. cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_REPEAT);
  333. needsRestore = true;
  334. }
  335. } else if (state->fillGradient) {
  336. if (state->globalAlpha < 1) {
  337. new_pattern = create_transparent_gradient(state->fillGradient, state->globalAlpha);
  338. if (new_pattern == NULL) {
  339. // failed to recognize gradient; Nan::ThrowError has already been called, so return from this fn.
  340. return;
  341. }
  342. cairo_pattern_set_filter(new_pattern, state->patternQuality);
  343. cairo_set_source(_context, new_pattern);
  344. cairo_pattern_destroy(new_pattern);
  345. } else {
  346. cairo_pattern_set_filter(state->fillGradient, state->patternQuality);
  347. cairo_set_source(_context, state->fillGradient);
  348. }
  349. } else {
  350. setSourceRGBA(state->fill);
  351. }
  352. if (preserve) {
  353. hasShadow()
  354. ? shadow(cairo_fill_preserve)
  355. : cairo_fill_preserve(_context);
  356. } else {
  357. hasShadow()
  358. ? shadow(cairo_fill)
  359. : cairo_fill(_context);
  360. }
  361. if (needsRestore) {
  362. cairo_restore(_context);
  363. }
  364. }
  365. /*
  366. * Stroke and apply shadow.
  367. */
  368. void
  369. Context2d::stroke(bool preserve) {
  370. cairo_pattern_t *new_pattern;
  371. if (state->strokePattern) {
  372. if (state->globalAlpha < 1) {
  373. new_pattern = create_transparent_pattern(state->strokePattern, state->globalAlpha);
  374. if (new_pattern == NULL) {
  375. // failed to allocate; Nan::ThrowError has already been called, so return from this fn.
  376. return;
  377. }
  378. cairo_set_source(_context, new_pattern);
  379. cairo_pattern_destroy(new_pattern);
  380. } else {
  381. cairo_pattern_set_filter(state->strokePattern, state->patternQuality);
  382. cairo_set_source(_context, state->strokePattern);
  383. }
  384. repeat_type_t repeat = Pattern::get_repeat_type_for_cairo_pattern(state->strokePattern);
  385. if (NO_REPEAT == repeat) {
  386. cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_NONE);
  387. } else {
  388. cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_REPEAT);
  389. }
  390. } else if (state->strokeGradient) {
  391. if (state->globalAlpha < 1) {
  392. new_pattern = create_transparent_gradient(state->strokeGradient, state->globalAlpha);
  393. if (new_pattern == NULL) {
  394. // failed to recognize gradient; Nan::ThrowError has already been called, so return from this fn.
  395. return;
  396. }
  397. cairo_pattern_set_filter(new_pattern, state->patternQuality);
  398. cairo_set_source(_context, new_pattern);
  399. cairo_pattern_destroy(new_pattern);
  400. } else {
  401. cairo_pattern_set_filter(state->strokeGradient, state->patternQuality);
  402. cairo_set_source(_context, state->strokeGradient);
  403. }
  404. } else {
  405. setSourceRGBA(state->stroke);
  406. }
  407. if (preserve) {
  408. hasShadow()
  409. ? shadow(cairo_stroke_preserve)
  410. : cairo_stroke_preserve(_context);
  411. } else {
  412. hasShadow()
  413. ? shadow(cairo_stroke)
  414. : cairo_stroke(_context);
  415. }
  416. }
  417. /*
  418. * Apply shadow with the given draw fn.
  419. */
  420. void
  421. Context2d::shadow(void (fn)(cairo_t *cr)) {
  422. cairo_path_t *path = cairo_copy_path_flat(_context);
  423. cairo_save(_context);
  424. // shadowOffset is unaffected by current transform
  425. cairo_matrix_t path_matrix;
  426. cairo_get_matrix(_context, &path_matrix);
  427. cairo_identity_matrix(_context);
  428. // Apply shadow
  429. cairo_push_group(_context);
  430. // No need to invoke blur if shadowBlur is 0
  431. if (state->shadowBlur) {
  432. // find out extent of path
  433. double x1, y1, x2, y2;
  434. if (fn == cairo_fill || fn == cairo_fill_preserve) {
  435. cairo_fill_extents(_context, &x1, &y1, &x2, &y2);
  436. } else {
  437. cairo_stroke_extents(_context, &x1, &y1, &x2, &y2);
  438. }
  439. // create new image surface that size + padding for blurring
  440. double dx = x2-x1, dy = y2-y1;
  441. cairo_user_to_device_distance(_context, &dx, &dy);
  442. int pad = state->shadowBlur * 2;
  443. cairo_surface_t *shadow_surface = cairo_image_surface_create(
  444. CAIRO_FORMAT_ARGB32,
  445. dx + 2 * pad,
  446. dy + 2 * pad);
  447. cairo_t *shadow_context = cairo_create(shadow_surface);
  448. // transform path to the right place
  449. cairo_translate(shadow_context, pad-x1, pad-y1);
  450. cairo_transform(shadow_context, &path_matrix);
  451. // set lineCap lineJoin lineDash
  452. cairo_set_line_cap(shadow_context, cairo_get_line_cap(_context));
  453. cairo_set_line_join(shadow_context, cairo_get_line_join(_context));
  454. double offset;
  455. int dashes = cairo_get_dash_count(_context);
  456. std::vector<double> a(dashes);
  457. cairo_get_dash(_context, a.data(), &offset);
  458. cairo_set_dash(shadow_context, a.data(), dashes, offset);
  459. // draw the path and blur
  460. cairo_set_line_width(shadow_context, cairo_get_line_width(_context));
  461. cairo_new_path(shadow_context);
  462. cairo_append_path(shadow_context, path);
  463. setSourceRGBA(shadow_context, state->shadow);
  464. fn(shadow_context);
  465. blur(shadow_surface, state->shadowBlur);
  466. // paint to original context
  467. cairo_set_source_surface(_context, shadow_surface,
  468. x1 - pad + state->shadowOffsetX + 1,
  469. y1 - pad + state->shadowOffsetY + 1);
  470. cairo_paint(_context);
  471. cairo_destroy(shadow_context);
  472. cairo_surface_destroy(shadow_surface);
  473. } else {
  474. // Offset first, then apply path's transform
  475. cairo_translate(
  476. _context
  477. , state->shadowOffsetX
  478. , state->shadowOffsetY);
  479. cairo_transform(_context, &path_matrix);
  480. // Apply shadow
  481. cairo_new_path(_context);
  482. cairo_append_path(_context, path);
  483. setSourceRGBA(state->shadow);
  484. fn(_context);
  485. }
  486. // Paint the shadow
  487. cairo_pop_group_to_source(_context);
  488. cairo_paint(_context);
  489. // Restore state
  490. cairo_restore(_context);
  491. cairo_new_path(_context);
  492. cairo_append_path(_context, path);
  493. fn(_context);
  494. cairo_path_destroy(path);
  495. }
  496. /*
  497. * Set source RGBA for the current context
  498. */
  499. void
  500. Context2d::setSourceRGBA(rgba_t color) {
  501. setSourceRGBA(_context, color);
  502. }
  503. /*
  504. * Set source RGBA
  505. */
  506. void
  507. Context2d::setSourceRGBA(cairo_t *ctx, rgba_t color) {
  508. cairo_set_source_rgba(
  509. ctx
  510. , color.r
  511. , color.g
  512. , color.b
  513. , color.a * state->globalAlpha);
  514. }
  515. /*
  516. * Check if the context has a drawable shadow.
  517. */
  518. bool
  519. Context2d::hasShadow() {
  520. return state->shadow.a
  521. && (state->shadowBlur || state->shadowOffsetX || state->shadowOffsetY);
  522. }
  523. /*
  524. * Blur the given surface with the given radius.
  525. */
  526. void
  527. Context2d::blur(cairo_surface_t *surface, int radius) {
  528. // Steve Hanov, 2009
  529. // Released into the public domain.
  530. radius = radius * 0.57735f + 0.5f;
  531. // get width, height
  532. int width = cairo_image_surface_get_width( surface );
  533. int height = cairo_image_surface_get_height( surface );
  534. unsigned* precalc =
  535. (unsigned*)malloc(width*height*sizeof(unsigned));
  536. cairo_surface_flush( surface );
  537. unsigned char* src = cairo_image_surface_get_data( surface );
  538. double mul=1.f/((radius*2)*(radius*2));
  539. int channel;
  540. // The number of times to perform the averaging. According to wikipedia,
  541. // three iterations is good enough to pass for a gaussian.
  542. const int MAX_ITERATIONS = 3;
  543. int iteration;
  544. for ( iteration = 0; iteration < MAX_ITERATIONS; iteration++ ) {
  545. for( channel = 0; channel < 4; channel++ ) {
  546. int x,y;
  547. // precomputation step.
  548. unsigned char* pix = src;
  549. unsigned* pre = precalc;
  550. pix += channel;
  551. for (y=0;y<height;y++) {
  552. for (x=0;x<width;x++) {
  553. int tot=pix[0];
  554. if (x>0) tot+=pre[-1];
  555. if (y>0) tot+=pre[-width];
  556. if (x>0 && y>0) tot-=pre[-width-1];
  557. *pre++=tot;
  558. pix += 4;
  559. }
  560. }
  561. // blur step.
  562. pix = src + (int)radius * width * 4 + (int)radius * 4 + channel;
  563. for (y=radius;y<height-radius;y++) {
  564. for (x=radius;x<width-radius;x++) {
  565. int l = x < radius ? 0 : x - radius;
  566. int t = y < radius ? 0 : y - radius;
  567. int r = x + radius >= width ? width - 1 : x + radius;
  568. int b = y + radius >= height ? height - 1 : y + radius;
  569. int tot = precalc[r+b*width] + precalc[l+t*width] -
  570. precalc[l+b*width] - precalc[r+t*width];
  571. *pix=(unsigned char)(tot*mul);
  572. pix += 4;
  573. }
  574. pix += (int)radius * 2 * 4;
  575. }
  576. }
  577. }
  578. cairo_surface_mark_dirty(surface);
  579. free(precalc);
  580. }
  581. /*
  582. * Initialize a new Context2d with the given canvas.
  583. */
  584. NAN_METHOD(Context2d::New) {
  585. if (!info.IsConstructCall()) {
  586. return Nan::ThrowTypeError("Class constructors cannot be invoked without 'new'");
  587. }
  588. if (!info[0]->IsObject())
  589. return Nan::ThrowTypeError("Canvas expected");
  590. Local<Object> obj = Nan::To<Object>(info[0]).ToLocalChecked();
  591. if (!Nan::New(Canvas::constructor)->HasInstance(obj))
  592. return Nan::ThrowTypeError("Canvas expected");
  593. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(obj);
  594. bool isImageBackend = canvas->backend()->getName() == "image";
  595. if (isImageBackend) {
  596. cairo_format_t format = ImageBackend::DEFAULT_FORMAT;
  597. if (info[1]->IsObject()) {
  598. Local<Object> ctxAttributes = Nan::To<Object>(info[1]).ToLocalChecked();
  599. Local<Value> pixelFormat = Nan::Get(ctxAttributes, Nan::New("pixelFormat").ToLocalChecked()).ToLocalChecked();
  600. if (pixelFormat->IsString()) {
  601. Nan::Utf8String utf8PixelFormat(pixelFormat);
  602. if (!strcmp(*utf8PixelFormat, "RGBA32")) format = CAIRO_FORMAT_ARGB32;
  603. else if (!strcmp(*utf8PixelFormat, "RGB24")) format = CAIRO_FORMAT_RGB24;
  604. else if (!strcmp(*utf8PixelFormat, "A8")) format = CAIRO_FORMAT_A8;
  605. else if (!strcmp(*utf8PixelFormat, "RGB16_565")) format = CAIRO_FORMAT_RGB16_565;
  606. else if (!strcmp(*utf8PixelFormat, "A1")) format = CAIRO_FORMAT_A1;
  607. #ifdef CAIRO_FORMAT_RGB30
  608. else if (!strcmp(utf8PixelFormat, "RGB30")) format = CAIRO_FORMAT_RGB30;
  609. #endif
  610. }
  611. // alpha: false forces use of RGB24
  612. Local<Value> alpha = Nan::Get(ctxAttributes, Nan::New("alpha").ToLocalChecked()).ToLocalChecked();
  613. if (alpha->IsBoolean() && !Nan::To<bool>(alpha).FromMaybe(false)) {
  614. format = CAIRO_FORMAT_RGB24;
  615. }
  616. }
  617. static_cast<ImageBackend*>(canvas->backend())->setFormat(format);
  618. }
  619. Context2d *context = new Context2d(canvas);
  620. context->Wrap(info.This());
  621. info.GetReturnValue().Set(info.This());
  622. }
  623. /*
  624. * Save some external modules as private references.
  625. */
  626. NAN_METHOD(Context2d::SaveExternalModules) {
  627. _DOMMatrix.Reset(Nan::To<Function>(info[0]).ToLocalChecked());
  628. _parseFont.Reset(Nan::To<Function>(info[1]).ToLocalChecked());
  629. }
  630. /*
  631. * Get format (string).
  632. */
  633. NAN_GETTER(Context2d::GetFormat) {
  634. CHECK_RECEIVER(Context2d.GetFormat);
  635. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  636. std::string pixelFormatString;
  637. switch (context->canvas()->backend()->getFormat()) {
  638. case CAIRO_FORMAT_ARGB32: pixelFormatString = "RGBA32"; break;
  639. case CAIRO_FORMAT_RGB24: pixelFormatString = "RGB24"; break;
  640. case CAIRO_FORMAT_A8: pixelFormatString = "A8"; break;
  641. case CAIRO_FORMAT_A1: pixelFormatString = "A1"; break;
  642. case CAIRO_FORMAT_RGB16_565: pixelFormatString = "RGB16_565"; break;
  643. #ifdef CAIRO_FORMAT_RGB30
  644. case CAIRO_FORMAT_RGB30: pixelFormatString = "RGB30"; break;
  645. #endif
  646. default: return info.GetReturnValue().SetNull();
  647. }
  648. info.GetReturnValue().Set(Nan::New<String>(pixelFormatString).ToLocalChecked());
  649. }
  650. /*
  651. * Create a new page.
  652. */
  653. NAN_METHOD(Context2d::AddPage) {
  654. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  655. if (context->canvas()->backend()->getName() != "pdf") {
  656. return Nan::ThrowError("only PDF canvases support .addPage()");
  657. }
  658. cairo_show_page(context->context());
  659. int width = Nan::To<int32_t>(info[0]).FromMaybe(0);
  660. int height = Nan::To<int32_t>(info[1]).FromMaybe(0);
  661. if (width < 1) width = context->canvas()->getWidth();
  662. if (height < 1) height = context->canvas()->getHeight();
  663. cairo_pdf_surface_set_size(context->canvas()->surface(), width, height);
  664. return;
  665. }
  666. /*
  667. * Put image data.
  668. *
  669. * - imageData, dx, dy
  670. * - imageData, dx, dy, sx, sy, sw, sh
  671. *
  672. */
  673. NAN_METHOD(Context2d::PutImageData) {
  674. if (!info[0]->IsObject())
  675. return Nan::ThrowTypeError("ImageData expected");
  676. Local<Object> obj = Nan::To<Object>(info[0]).ToLocalChecked();
  677. if (!Nan::New(ImageData::constructor)->HasInstance(obj))
  678. return Nan::ThrowTypeError("ImageData expected");
  679. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  680. ImageData *imageData = Nan::ObjectWrap::Unwrap<ImageData>(obj);
  681. uint8_t *src = imageData->data();
  682. uint8_t *dst = context->canvas()->data();
  683. int dstStride = context->canvas()->stride();
  684. int Bpp = dstStride / context->canvas()->getWidth();
  685. int srcStride = Bpp * imageData->width();
  686. int sx = 0
  687. , sy = 0
  688. , sw = 0
  689. , sh = 0
  690. , dx = Nan::To<int32_t>(info[1]).FromMaybe(0)
  691. , dy = Nan::To<int32_t>(info[2]).FromMaybe(0)
  692. , rows
  693. , cols;
  694. switch (info.Length()) {
  695. // imageData, dx, dy
  696. case 3:
  697. sw = imageData->width();
  698. sh = imageData->height();
  699. break;
  700. // imageData, dx, dy, sx, sy, sw, sh
  701. case 7:
  702. sx = Nan::To<int32_t>(info[3]).FromMaybe(0);
  703. sy = Nan::To<int32_t>(info[4]).FromMaybe(0);
  704. sw = Nan::To<int32_t>(info[5]).FromMaybe(0);
  705. sh = Nan::To<int32_t>(info[6]).FromMaybe(0);
  706. // fix up negative height, width
  707. if (sw < 0) sx += sw, sw = -sw;
  708. if (sh < 0) sy += sh, sh = -sh;
  709. // clamp the left edge
  710. if (sx < 0) sw += sx, sx = 0;
  711. if (sy < 0) sh += sy, sy = 0;
  712. // clamp the right edge
  713. if (sx + sw > imageData->width()) sw = imageData->width() - sx;
  714. if (sy + sh > imageData->height()) sh = imageData->height() - sy;
  715. // start destination at source offset
  716. dx += sx;
  717. dy += sy;
  718. break;
  719. default:
  720. return Nan::ThrowError("invalid arguments");
  721. }
  722. // chop off outlying source data
  723. if (dx < 0) sw += dx, sx -= dx, dx = 0;
  724. if (dy < 0) sh += dy, sy -= dy, dy = 0;
  725. // clamp width at canvas size
  726. // Need to wrap std::min calls using parens to prevent macro expansion on
  727. // windows. See http://stackoverflow.com/questions/5004858/stdmin-gives-error
  728. cols = (std::min)(sw, context->canvas()->getWidth() - dx);
  729. rows = (std::min)(sh, context->canvas()->getHeight() - dy);
  730. if (cols <= 0 || rows <= 0) return;
  731. switch (context->canvas()->backend()->getFormat()) {
  732. case CAIRO_FORMAT_ARGB32: {
  733. src += sy * srcStride + sx * 4;
  734. dst += dstStride * dy + 4 * dx;
  735. for (int y = 0; y < rows; ++y) {
  736. uint8_t *dstRow = dst;
  737. uint8_t *srcRow = src;
  738. for (int x = 0; x < cols; ++x) {
  739. // rgba
  740. uint8_t r = *srcRow++;
  741. uint8_t g = *srcRow++;
  742. uint8_t b = *srcRow++;
  743. uint8_t a = *srcRow++;
  744. // argb
  745. // performance optimization: fully transparent/opaque pixels can be
  746. // processed more efficiently.
  747. if (a == 0) {
  748. *dstRow++ = 0;
  749. *dstRow++ = 0;
  750. *dstRow++ = 0;
  751. *dstRow++ = 0;
  752. } else if (a == 255) {
  753. *dstRow++ = b;
  754. *dstRow++ = g;
  755. *dstRow++ = r;
  756. *dstRow++ = a;
  757. } else {
  758. float alpha = (float)a / 255;
  759. *dstRow++ = b * alpha;
  760. *dstRow++ = g * alpha;
  761. *dstRow++ = r * alpha;
  762. *dstRow++ = a;
  763. }
  764. }
  765. dst += dstStride;
  766. src += srcStride;
  767. }
  768. break;
  769. }
  770. case CAIRO_FORMAT_RGB24: {
  771. src += sy * srcStride + sx * 4;
  772. dst += dstStride * dy + 4 * dx;
  773. for (int y = 0; y < rows; ++y) {
  774. uint8_t *dstRow = dst;
  775. uint8_t *srcRow = src;
  776. for (int x = 0; x < cols; ++x) {
  777. // rgba
  778. uint8_t r = *srcRow++;
  779. uint8_t g = *srcRow++;
  780. uint8_t b = *srcRow++;
  781. srcRow++;
  782. // argb
  783. *dstRow++ = b;
  784. *dstRow++ = g;
  785. *dstRow++ = r;
  786. *dstRow++ = 255;
  787. }
  788. dst += dstStride;
  789. src += srcStride;
  790. }
  791. break;
  792. }
  793. case CAIRO_FORMAT_A8: {
  794. src += sy * srcStride + sx;
  795. dst += dstStride * dy + dx;
  796. if (srcStride == dstStride && cols == dstStride) {
  797. // fast path: strides are the same and doing a full-width put
  798. memcpy(dst, src, cols * rows);
  799. } else {
  800. for (int y = 0; y < rows; ++y) {
  801. memcpy(dst, src, cols);
  802. dst += dstStride;
  803. src += srcStride;
  804. }
  805. }
  806. break;
  807. }
  808. case CAIRO_FORMAT_A1: {
  809. // TODO Should this be totally packed, or maintain a stride divisible by 4?
  810. Nan::ThrowError("putImageData for CANVAS_FORMAT_A1 is not yet implemented");
  811. break;
  812. }
  813. case CAIRO_FORMAT_RGB16_565: {
  814. src += sy * srcStride + sx * 2;
  815. dst += dstStride * dy + 2 * dx;
  816. for (int y = 0; y < rows; ++y) {
  817. memcpy(dst, src, cols * 2);
  818. dst += dstStride;
  819. src += srcStride;
  820. }
  821. break;
  822. }
  823. #ifdef CAIRO_FORMAT_RGB30
  824. case CAIRO_FORMAT_RGB30: {
  825. // TODO
  826. Nan::ThrowError("putImageData for CANVAS_FORMAT_RGB30 is not yet implemented");
  827. break;
  828. }
  829. #endif
  830. default: {
  831. Nan::ThrowError("Invalid pixel format or not an image canvas");
  832. return;
  833. }
  834. }
  835. cairo_surface_mark_dirty_rectangle(
  836. context->canvas()->surface()
  837. , dx
  838. , dy
  839. , cols
  840. , rows);
  841. }
  842. /*
  843. * Get image data.
  844. *
  845. * - sx, sy, sw, sh
  846. *
  847. */
  848. NAN_METHOD(Context2d::GetImageData) {
  849. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  850. Canvas *canvas = context->canvas();
  851. int sx = Nan::To<int32_t>(info[0]).FromMaybe(0);
  852. int sy = Nan::To<int32_t>(info[1]).FromMaybe(0);
  853. int sw = Nan::To<int32_t>(info[2]).FromMaybe(0);
  854. int sh = Nan::To<int32_t>(info[3]).FromMaybe(0);
  855. if (!sw)
  856. return Nan::ThrowError("IndexSizeError: The source width is 0.");
  857. if (!sh)
  858. return Nan::ThrowError("IndexSizeError: The source height is 0.");
  859. int width = canvas->getWidth();
  860. int height = canvas->getHeight();
  861. if (!width)
  862. return Nan::ThrowTypeError("Canvas width is 0");
  863. if (!height)
  864. return Nan::ThrowTypeError("Canvas height is 0");
  865. // WebKit and Firefox have this behavior:
  866. // Flip the coordinates so the origin is top/left-most:
  867. if (sw < 0) {
  868. sx += sw;
  869. sw = -sw;
  870. }
  871. if (sh < 0) {
  872. sy += sh;
  873. sh = -sh;
  874. }
  875. if (sx + sw > width) sw = width - sx;
  876. if (sy + sh > height) sh = height - sy;
  877. // WebKit/moz functionality. node-canvas used to return in either case.
  878. if (sw <= 0) sw = 1;
  879. if (sh <= 0) sh = 1;
  880. // Non-compliant. "Pixels outside the canvas must be returned as transparent
  881. // black." This instead clips the returned array to the canvas area.
  882. if (sx < 0) {
  883. sw += sx;
  884. sx = 0;
  885. }
  886. if (sy < 0) {
  887. sh += sy;
  888. sy = 0;
  889. }
  890. int srcStride = canvas->stride();
  891. int bpp = srcStride / width;
  892. int size = sw * sh * bpp;
  893. int dstStride = sw * bpp;
  894. uint8_t *src = canvas->data();
  895. Local<ArrayBuffer> buffer = ArrayBuffer::New(Isolate::GetCurrent(), size);
  896. Local<TypedArray> dataArray;
  897. if (canvas->backend()->getFormat() == CAIRO_FORMAT_RGB16_565) {
  898. dataArray = Uint16Array::New(buffer, 0, size >> 1);
  899. } else {
  900. dataArray = Uint8ClampedArray::New(buffer, 0, size);
  901. }
  902. Nan::TypedArrayContents<uint8_t> typedArrayContents(dataArray);
  903. uint8_t* dst = *typedArrayContents;
  904. switch (canvas->backend()->getFormat()) {
  905. case CAIRO_FORMAT_ARGB32: {
  906. // Rearrange alpha (argb -> rgba), undo alpha pre-multiplication,
  907. // and store in big-endian format
  908. for (int y = 0; y < sh; ++y) {
  909. uint32_t *row = (uint32_t *)(src + srcStride * (y + sy));
  910. for (int x = 0; x < sw; ++x) {
  911. int bx = x * 4;
  912. uint32_t *pixel = row + x + sx;
  913. uint8_t a = *pixel >> 24;
  914. uint8_t r = *pixel >> 16;
  915. uint8_t g = *pixel >> 8;
  916. uint8_t b = *pixel;
  917. dst[bx + 3] = a;
  918. // Performance optimization: fully transparent/opaque pixels can be
  919. // processed more efficiently.
  920. if (a == 0 || a == 255) {
  921. dst[bx + 0] = r;
  922. dst[bx + 1] = g;
  923. dst[bx + 2] = b;
  924. } else {
  925. // Undo alpha pre-multiplication
  926. float alphaR = (float)255 / a;
  927. dst[bx + 0] = (int)((float)r * alphaR);
  928. dst[bx + 1] = (int)((float)g * alphaR);
  929. dst[bx + 2] = (int)((float)b * alphaR);
  930. }
  931. }
  932. dst += dstStride;
  933. }
  934. break;
  935. }
  936. case CAIRO_FORMAT_RGB24: {
  937. // Rearrange alpha (argb -> rgba) and store in big-endian format
  938. for (int y = 0; y < sh; ++y) {
  939. uint32_t *row = (uint32_t *)(src + srcStride * (y + sy));
  940. for (int x = 0; x < sw; ++x) {
  941. int bx = x * 4;
  942. uint32_t *pixel = row + x + sx;
  943. uint8_t r = *pixel >> 16;
  944. uint8_t g = *pixel >> 8;
  945. uint8_t b = *pixel;
  946. dst[bx + 0] = r;
  947. dst[bx + 1] = g;
  948. dst[bx + 2] = b;
  949. dst[bx + 3] = 255;
  950. }
  951. dst += dstStride;
  952. }
  953. break;
  954. }
  955. case CAIRO_FORMAT_A8: {
  956. for (int y = 0; y < sh; ++y) {
  957. uint8_t *row = (uint8_t *)(src + srcStride * (y + sy));
  958. memcpy(dst, row + sx, dstStride);
  959. dst += dstStride;
  960. }
  961. break;
  962. }
  963. case CAIRO_FORMAT_A1: {
  964. // TODO Should this be totally packed, or maintain a stride divisible by 4?
  965. Nan::ThrowError("getImageData for CANVAS_FORMAT_A1 is not yet implemented");
  966. break;
  967. }
  968. case CAIRO_FORMAT_RGB16_565: {
  969. for (int y = 0; y < sh; ++y) {
  970. uint16_t *row = (uint16_t *)(src + srcStride * (y + sy));
  971. memcpy(dst, row + sx, dstStride);
  972. dst += dstStride;
  973. }
  974. break;
  975. }
  976. #ifdef CAIRO_FORMAT_RGB30
  977. case CAIRO_FORMAT_RGB30: {
  978. // TODO
  979. Nan::ThrowError("getImageData for CANVAS_FORMAT_RGB30 is not yet implemented");
  980. break;
  981. }
  982. #endif
  983. default: {
  984. // Unlikely
  985. Nan::ThrowError("Invalid pixel format or not an image canvas");
  986. return;
  987. }
  988. }
  989. const int argc = 3;
  990. Local<Int32> swHandle = Nan::New(sw);
  991. Local<Int32> shHandle = Nan::New(sh);
  992. Local<Value> argv[argc] = { dataArray, swHandle, shHandle };
  993. Local<Function> ctor = Nan::GetFunction(Nan::New(ImageData::constructor)).ToLocalChecked();
  994. Local<Object> instance = Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
  995. info.GetReturnValue().Set(instance);
  996. }
  997. /**
  998. * Create `ImageData` with the given dimensions or
  999. * `ImageData` instance for dimensions.
  1000. */
  1001. NAN_METHOD(Context2d::CreateImageData){
  1002. Isolate *iso = Isolate::GetCurrent();
  1003. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1004. Canvas *canvas = context->canvas();
  1005. int32_t width, height;
  1006. if (info[0]->IsObject()) {
  1007. Local<Object> obj = Nan::To<Object>(info[0]).ToLocalChecked();
  1008. width = Nan::To<int32_t>(Nan::Get(obj, Nan::New("width").ToLocalChecked()).ToLocalChecked()).FromMaybe(0);
  1009. height = Nan::To<int32_t>(Nan::Get(obj, Nan::New("height").ToLocalChecked()).ToLocalChecked()).FromMaybe(0);
  1010. } else {
  1011. width = Nan::To<int32_t>(info[0]).FromMaybe(0);
  1012. height = Nan::To<int32_t>(info[1]).FromMaybe(0);
  1013. }
  1014. int stride = canvas->stride();
  1015. double Bpp = static_cast<double>(stride) / canvas->getWidth();
  1016. int nBytes = static_cast<int>(Bpp * width * height + .5);
  1017. Local<ArrayBuffer> ab = ArrayBuffer::New(iso, nBytes);
  1018. Local<Object> arr;
  1019. if (canvas->backend()->getFormat() == CAIRO_FORMAT_RGB16_565)
  1020. arr = Uint16Array::New(ab, 0, nBytes / 2);
  1021. else
  1022. arr = Uint8ClampedArray::New(ab, 0, nBytes);
  1023. const int argc = 3;
  1024. Local<Value> argv[argc] = { arr, Nan::New(width), Nan::New(height) };
  1025. Local<Function> ctor = Nan::GetFunction(Nan::New(ImageData::constructor)).ToLocalChecked();
  1026. Local<Object> instance = Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
  1027. info.GetReturnValue().Set(instance);
  1028. }
  1029. /*
  1030. * Take a transform matrix and return its components
  1031. * 0: angle, 1: scaleX, 2: scaleY, 3: skewX, 4: translateX, 5: translateY
  1032. */
  1033. void decompose_matrix(cairo_matrix_t matrix, double *destination) {
  1034. double denom = pow(matrix.xx, 2) + pow(matrix.yx, 2);
  1035. destination[0] = atan2(matrix.yx, matrix.xx);
  1036. destination[1] = sqrt(denom);
  1037. destination[2] = (matrix.xx * matrix.yy - matrix.xy * matrix.yx) / destination[1];
  1038. destination[3] = atan2(matrix.xx * matrix.xy + matrix.yx * matrix.yy, denom);
  1039. destination[4] = matrix.x0;
  1040. destination[5] = matrix.y0;
  1041. }
  1042. /*
  1043. * Draw image src image to the destination (context).
  1044. *
  1045. * - dx, dy
  1046. * - dx, dy, dw, dh
  1047. * - sx, sy, sw, sh, dx, dy, dw, dh
  1048. *
  1049. */
  1050. NAN_METHOD(Context2d::DrawImage) {
  1051. int infoLen = info.Length();
  1052. if (infoLen != 3 && infoLen != 5 && infoLen != 9)
  1053. return Nan::ThrowTypeError("Invalid arguments");
  1054. if (!info[0]->IsObject())
  1055. return Nan::ThrowTypeError("The first argument must be an object");
  1056. double args[8];
  1057. if(!checkArgs(info, args, infoLen - 1, 1))
  1058. return;
  1059. double sx = 0
  1060. , sy = 0
  1061. , sw = 0
  1062. , sh = 0
  1063. , dx = 0
  1064. , dy = 0
  1065. , dw = 0
  1066. , dh = 0
  1067. , source_w = 0
  1068. , source_h = 0;
  1069. cairo_surface_t *surface;
  1070. Local<Object> obj = Nan::To<Object>(info[0]).ToLocalChecked();
  1071. // Image
  1072. if (Nan::New(Image::constructor)->HasInstance(obj)) {
  1073. Image *img = Nan::ObjectWrap::Unwrap<Image>(obj);
  1074. if (!img->isComplete()) {
  1075. return Nan::ThrowError("Image given has not completed loading");
  1076. }
  1077. source_w = sw = img->width;
  1078. source_h = sh = img->height;
  1079. surface = img->surface();
  1080. // Canvas
  1081. } else if (Nan::New(Canvas::constructor)->HasInstance(obj)) {
  1082. Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(obj);
  1083. source_w = sw = canvas->getWidth();
  1084. source_h = sh = canvas->getHeight();
  1085. surface = canvas->surface();
  1086. // Invalid
  1087. } else {
  1088. return Nan::ThrowTypeError("Image or Canvas expected");
  1089. }
  1090. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1091. cairo_t *ctx = context->context();
  1092. // Arguments
  1093. switch (infoLen) {
  1094. // img, sx, sy, sw, sh, dx, dy, dw, dh
  1095. case 9:
  1096. sx = args[0];
  1097. sy = args[1];
  1098. sw = args[2];
  1099. sh = args[3];
  1100. dx = args[4];
  1101. dy = args[5];
  1102. dw = args[6];
  1103. dh = args[7];
  1104. break;
  1105. // img, dx, dy, dw, dh
  1106. case 5:
  1107. dx = args[0];
  1108. dy = args[1];
  1109. dw = args[2];
  1110. dh = args[3];
  1111. break;
  1112. // img, dx, dy
  1113. case 3:
  1114. dx = args[0];
  1115. dy = args[1];
  1116. dw = sw;
  1117. dh = sh;
  1118. break;
  1119. }
  1120. if (!(sw && sh && dw && dh))
  1121. return;
  1122. // Start draw
  1123. cairo_save(ctx);
  1124. cairo_matrix_t matrix;
  1125. double transforms[6];
  1126. cairo_get_matrix(context->context(), &matrix);
  1127. decompose_matrix(matrix, transforms);
  1128. // extract the scale value from the current transform so that we know how many pixels we
  1129. // need for our extra canvas in the drawImage operation.
  1130. double current_scale_x = std::abs(transforms[1]);
  1131. double current_scale_y = std::abs(transforms[2]);
  1132. double extra_dx = 0;
  1133. double extra_dy = 0;
  1134. double fx = dw / sw * current_scale_x; // transforms[1] is scale on X
  1135. double fy = dh / sh * current_scale_y; // transforms[2] is scale on X
  1136. bool needScale = dw != sw || dh != sh;
  1137. bool needCut = sw != source_w || sh != source_h || sx < 0 || sy < 0;
  1138. bool sameCanvas = surface == context->canvas()->surface();
  1139. bool needsExtraSurface = sameCanvas || needCut || needScale;
  1140. cairo_surface_t *surfTemp = NULL;
  1141. cairo_t *ctxTemp = NULL;
  1142. if (needsExtraSurface) {
  1143. // we want to create the extra surface as small as possible.
  1144. // fx and fy are the total scaling we need to apply to sw, sh.
  1145. // from sw and sh we want to remove the part that is outside the source_w and soruce_h
  1146. double real_w = sw;
  1147. double real_h = sh;
  1148. double translate_x = 0;
  1149. double translate_y = 0;
  1150. // if sx or sy are negative, a part of the area represented by sw and sh is empty
  1151. // because there are empty pixels, so we cut it out.
  1152. // On the other hand if sx or sy are positive, but sw and sh extend outside the real
  1153. // source pixels, we cut the area in that case too.
  1154. if (sx < 0) {
  1155. extra_dx = -sx * fx;
  1156. real_w = sw + sx;
  1157. } else if (sx + sw > source_w) {
  1158. real_w = sw - (sx + sw - source_w);
  1159. }
  1160. if (sy < 0) {
  1161. extra_dy = -sy * fy;
  1162. real_h = sh + sy;
  1163. } else if (sy + sh > source_h) {
  1164. real_h = sh - (sy + sh - source_h);
  1165. }
  1166. // if after cutting we are still bigger than source pixels, we restrict again
  1167. if (real_w > source_w) {
  1168. real_w = source_w;
  1169. }
  1170. if (real_h > source_h) {
  1171. real_h = source_h;
  1172. }
  1173. // TODO: find a way to limit the surfTemp to real_w and real_h if fx and fy are bigger than 1.
  1174. // there are no more pixel than the one available in the source, no need to create a bigger surface.
  1175. surfTemp = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, round(real_w * fx), round(real_h * fy));
  1176. ctxTemp = cairo_create(surfTemp);
  1177. cairo_scale(ctxTemp, fx, fy);
  1178. if (sx > 0) {
  1179. translate_x = sx;
  1180. }
  1181. if (sy > 0) {
  1182. translate_y = sy;
  1183. }
  1184. cairo_set_source_surface(ctxTemp, surface, -translate_x, -translate_y);
  1185. cairo_pattern_set_filter(cairo_get_source(ctxTemp), context->state->imageSmoothingEnabled ? context->state->patternQuality : CAIRO_FILTER_NEAREST);
  1186. cairo_pattern_set_extend(cairo_get_source(ctxTemp), CAIRO_EXTEND_REFLECT);
  1187. cairo_paint_with_alpha(ctxTemp, 1);
  1188. surface = surfTemp;
  1189. }
  1190. // apply shadow if there is one
  1191. if (context->hasShadow()) {
  1192. if(context->state->shadowBlur) {
  1193. // we need to create a new surface in order to blur
  1194. int pad = context->state->shadowBlur * 2;
  1195. cairo_surface_t *shadow_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dw + 2 * pad, dh + 2 * pad);
  1196. cairo_t *shadow_context = cairo_create(shadow_surface);
  1197. // mask and blur
  1198. context->setSourceRGBA(shadow_context, context->state->shadow);
  1199. cairo_mask_surface(shadow_context, surface, pad, pad);
  1200. context->blur(shadow_surface, context->state->shadowBlur);
  1201. // paint
  1202. // @note: ShadowBlur looks different in each browser. This implementation matches chrome as close as possible.
  1203. // The 1.4 offset comes from visual tests with Chrome. I have read the spec and part of the shadowBlur
  1204. // implementation, and its not immediately clear why an offset is necessary, but without it, the result
  1205. // in chrome is different.
  1206. cairo_set_source_surface(ctx, shadow_surface,
  1207. dx + context->state->shadowOffsetX - pad + 1.4,
  1208. dy + context->state->shadowOffsetY - pad + 1.4);
  1209. cairo_paint(ctx);
  1210. // cleanup
  1211. cairo_destroy(shadow_context);
  1212. cairo_surface_destroy(shadow_surface);
  1213. } else {
  1214. context->setSourceRGBA(context->state->shadow);
  1215. cairo_mask_surface(ctx, surface,
  1216. dx + (context->state->shadowOffsetX),
  1217. dy + (context->state->shadowOffsetY));
  1218. }
  1219. }
  1220. double scaled_dx = dx;
  1221. double scaled_dy = dy;
  1222. if (needsExtraSurface && (current_scale_x != 1 || current_scale_y != 1)) {
  1223. // in this case our surface contains already current_scale_x, we need to scale back
  1224. cairo_scale(ctx, 1 / current_scale_x, 1 / current_scale_y);
  1225. scaled_dx *= current_scale_x;
  1226. scaled_dy *= current_scale_y;
  1227. }
  1228. // Paint
  1229. cairo_set_source_surface(ctx, surface, scaled_dx + extra_dx, scaled_dy + extra_dy);
  1230. cairo_pattern_set_filter(cairo_get_source(ctx), context->state->imageSmoothingEnabled ? context->state->patternQuality : CAIRO_FILTER_NEAREST);
  1231. cairo_pattern_set_extend(cairo_get_source(ctx), CAIRO_EXTEND_NONE);
  1232. cairo_paint_with_alpha(ctx, context->state->globalAlpha);
  1233. cairo_restore(ctx);
  1234. if (needsExtraSurface) {
  1235. cairo_destroy(ctxTemp);
  1236. cairo_surface_destroy(surfTemp);
  1237. }
  1238. }
  1239. /*
  1240. * Get global alpha.
  1241. */
  1242. NAN_GETTER(Context2d::GetGlobalAlpha) {
  1243. CHECK_RECEIVER(Context2d.GetGlobalAlpha);
  1244. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1245. info.GetReturnValue().Set(Nan::New<Number>(context->state->globalAlpha));
  1246. }
  1247. /*
  1248. * Set global alpha.
  1249. */
  1250. NAN_SETTER(Context2d::SetGlobalAlpha) {
  1251. CHECK_RECEIVER(Context2d.SetGlobalAlpha);
  1252. double n = Nan::To<double>(value).FromMaybe(0);
  1253. if (n >= 0 && n <= 1) {
  1254. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1255. context->state->globalAlpha = n;
  1256. }
  1257. }
  1258. /*
  1259. * Get global composite operation.
  1260. */
  1261. NAN_GETTER(Context2d::GetGlobalCompositeOperation) {
  1262. CHECK_RECEIVER(Context2d.GetGlobalCompositeOperation);
  1263. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1264. cairo_t *ctx = context->context();
  1265. const char *op = "source-over";
  1266. switch (cairo_get_operator(ctx)) {
  1267. // composite modes:
  1268. case CAIRO_OPERATOR_CLEAR: op = "clear"; break;
  1269. case CAIRO_OPERATOR_SOURCE: op = "copy"; break;
  1270. case CAIRO_OPERATOR_DEST: op = "destination"; break;
  1271. case CAIRO_OPERATOR_OVER: op = "source-over"; break;
  1272. case CAIRO_OPERATOR_DEST_OVER: op = "destination-over"; break;
  1273. case CAIRO_OPERATOR_IN: op = "source-in"; break;
  1274. case CAIRO_OPERATOR_DEST_IN: op = "destination-in"; break;
  1275. case CAIRO_OPERATOR_OUT: op = "source-out"; break;
  1276. case CAIRO_OPERATOR_DEST_OUT: op = "destination-out"; break;
  1277. case CAIRO_OPERATOR_ATOP: op = "source-atop"; break;
  1278. case CAIRO_OPERATOR_DEST_ATOP: op = "destination-atop"; break;
  1279. case CAIRO_OPERATOR_XOR: op = "xor"; break;
  1280. case CAIRO_OPERATOR_ADD: op = "lighter"; break;
  1281. // blend modes:
  1282. // Note: "source-over" and "normal" are synonyms. Chrome and FF both report
  1283. // "source-over" after setting gCO to "normal".
  1284. // case CAIRO_OPERATOR_OVER: op = "normal";
  1285. case CAIRO_OPERATOR_MULTIPLY: op = "multiply"; break;
  1286. case CAIRO_OPERATOR_SCREEN: op = "screen"; break;
  1287. case CAIRO_OPERATOR_OVERLAY: op = "overlay"; break;
  1288. case CAIRO_OPERATOR_DARKEN: op = "darken"; break;
  1289. case CAIRO_OPERATOR_LIGHTEN: op = "lighten"; break;
  1290. case CAIRO_OPERATOR_COLOR_DODGE: op = "color-dodge"; break;
  1291. case CAIRO_OPERATOR_COLOR_BURN: op = "color-burn"; break;
  1292. case CAIRO_OPERATOR_HARD_LIGHT: op = "hard-light"; break;
  1293. case CAIRO_OPERATOR_SOFT_LIGHT: op = "soft-light"; break;
  1294. case CAIRO_OPERATOR_DIFFERENCE: op = "difference"; break;
  1295. case CAIRO_OPERATOR_EXCLUSION: op = "exclusion"; break;
  1296. case CAIRO_OPERATOR_HSL_HUE: op = "hue"; break;
  1297. case CAIRO_OPERATOR_HSL_SATURATION: op = "saturation"; break;
  1298. case CAIRO_OPERATOR_HSL_COLOR: op = "color"; break;
  1299. case CAIRO_OPERATOR_HSL_LUMINOSITY: op = "luminosity"; break;
  1300. // non-standard:
  1301. case CAIRO_OPERATOR_SATURATE: op = "saturate"; break;
  1302. }
  1303. info.GetReturnValue().Set(Nan::New(op).ToLocalChecked());
  1304. }
  1305. /*
  1306. * Set pattern quality.
  1307. */
  1308. NAN_SETTER(Context2d::SetPatternQuality) {
  1309. CHECK_RECEIVER(Context2d.SetPatternQuality);
  1310. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1311. Nan::Utf8String quality(Nan::To<String>(value).ToLocalChecked());
  1312. if (0 == strcmp("fast", *quality)) {
  1313. context->state->patternQuality = CAIRO_FILTER_FAST;
  1314. } else if (0 == strcmp("good", *quality)) {
  1315. context->state->patternQuality = CAIRO_FILTER_GOOD;
  1316. } else if (0 == strcmp("best", *quality)) {
  1317. context->state->patternQuality = CAIRO_FILTER_BEST;
  1318. } else if (0 == strcmp("nearest", *quality)) {
  1319. context->state->patternQuality = CAIRO_FILTER_NEAREST;
  1320. } else if (0 == strcmp("bilinear", *quality)) {
  1321. context->state->patternQuality = CAIRO_FILTER_BILINEAR;
  1322. }
  1323. }
  1324. /*
  1325. * Get pattern quality.
  1326. */
  1327. NAN_GETTER(Context2d::GetPatternQuality) {
  1328. CHECK_RECEIVER(Context2d.GetPatternQuality);
  1329. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1330. const char *quality;
  1331. switch (context->state->patternQuality) {
  1332. case CAIRO_FILTER_FAST: quality = "fast"; break;
  1333. case CAIRO_FILTER_BEST: quality = "best"; break;
  1334. case CAIRO_FILTER_NEAREST: quality = "nearest"; break;
  1335. case CAIRO_FILTER_BILINEAR: quality = "bilinear"; break;
  1336. default: quality = "good";
  1337. }
  1338. info.GetReturnValue().Set(Nan::New(quality).ToLocalChecked());
  1339. }
  1340. /*
  1341. * Set ImageSmoothingEnabled value.
  1342. */
  1343. NAN_SETTER(Context2d::SetImageSmoothingEnabled) {
  1344. CHECK_RECEIVER(Context2d.SetImageSmoothingEnabled);
  1345. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1346. context->state->imageSmoothingEnabled = Nan::To<bool>(value).FromMaybe(false);
  1347. }
  1348. /*
  1349. * Get pattern quality.
  1350. */
  1351. NAN_GETTER(Context2d::GetImageSmoothingEnabled) {
  1352. CHECK_RECEIVER(Context2d.GetImageSmoothingEnabled);
  1353. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1354. info.GetReturnValue().Set(Nan::New<Boolean>(context->state->imageSmoothingEnabled));
  1355. }
  1356. /*
  1357. * Set global composite operation.
  1358. */
  1359. NAN_SETTER(Context2d::SetGlobalCompositeOperation) {
  1360. CHECK_RECEIVER(Context2d.SetGlobalCompositeOperation);
  1361. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1362. cairo_t *ctx = context->context();
  1363. Nan::Utf8String opStr(Nan::To<String>(value).ToLocalChecked()); // Unlike CSS colors, this *is* case-sensitive
  1364. const std::map<std::string, cairo_operator_t> blendmodes = {
  1365. // composite modes:
  1366. {"clear", CAIRO_OPERATOR_CLEAR},
  1367. {"copy", CAIRO_OPERATOR_SOURCE},
  1368. {"destination", CAIRO_OPERATOR_DEST}, // this seems to have been omitted from the spec
  1369. {"source-over", CAIRO_OPERATOR_OVER},
  1370. {"destination-over", CAIRO_OPERATOR_DEST_OVER},
  1371. {"source-in", CAIRO_OPERATOR_IN},
  1372. {"destination-in", CAIRO_OPERATOR_DEST_IN},
  1373. {"source-out", CAIRO_OPERATOR_OUT},
  1374. {"destination-out", CAIRO_OPERATOR_DEST_OUT},
  1375. {"source-atop", CAIRO_OPERATOR_ATOP},
  1376. {"destination-atop", CAIRO_OPERATOR_DEST_ATOP},
  1377. {"xor", CAIRO_OPERATOR_XOR},
  1378. {"lighter", CAIRO_OPERATOR_ADD},
  1379. // blend modes:
  1380. {"normal", CAIRO_OPERATOR_OVER},
  1381. {"multiply", CAIRO_OPERATOR_MULTIPLY},
  1382. {"screen", CAIRO_OPERATOR_SCREEN},
  1383. {"overlay", CAIRO_OPERATOR_OVERLAY},
  1384. {"darken", CAIRO_OPERATOR_DARKEN},
  1385. {"lighten", CAIRO_OPERATOR_LIGHTEN},
  1386. {"color-dodge", CAIRO_OPERATOR_COLOR_DODGE},
  1387. {"color-burn", CAIRO_OPERATOR_COLOR_BURN},
  1388. {"hard-light", CAIRO_OPERATOR_HARD_LIGHT},
  1389. {"soft-light", CAIRO_OPERATOR_SOFT_LIGHT},
  1390. {"difference", CAIRO_OPERATOR_DIFFERENCE},
  1391. {"exclusion", CAIRO_OPERATOR_EXCLUSION},
  1392. {"hue", CAIRO_OPERATOR_HSL_HUE},
  1393. {"saturation", CAIRO_OPERATOR_HSL_SATURATION},
  1394. {"color", CAIRO_OPERATOR_HSL_COLOR},
  1395. {"luminosity", CAIRO_OPERATOR_HSL_LUMINOSITY},
  1396. // non-standard:
  1397. {"saturate", CAIRO_OPERATOR_SATURATE}
  1398. };
  1399. auto op = blendmodes.find(*opStr);
  1400. if (op != blendmodes.end()) cairo_set_operator(ctx, op->second);
  1401. }
  1402. /*
  1403. * Get shadow offset x.
  1404. */
  1405. NAN_GETTER(Context2d::GetShadowOffsetX) {
  1406. CHECK_RECEIVER(Context2d.GetShadowOffsetX);
  1407. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1408. info.GetReturnValue().Set(Nan::New<Number>(context->state->shadowOffsetX));
  1409. }
  1410. /*
  1411. * Set shadow offset x.
  1412. */
  1413. NAN_SETTER(Context2d::SetShadowOffsetX) {
  1414. CHECK_RECEIVER(Context2d.SetShadowOffsetX);
  1415. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1416. context->state->shadowOffsetX = Nan::To<double>(value).FromMaybe(0);
  1417. }
  1418. /*
  1419. * Get shadow offset y.
  1420. */
  1421. NAN_GETTER(Context2d::GetShadowOffsetY) {
  1422. CHECK_RECEIVER(Context2d.GetShadowOffsetY);
  1423. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1424. info.GetReturnValue().Set(Nan::New<Number>(context->state->shadowOffsetY));
  1425. }
  1426. /*
  1427. * Set shadow offset y.
  1428. */
  1429. NAN_SETTER(Context2d::SetShadowOffsetY) {
  1430. CHECK_RECEIVER(Context2d.SetShadowOffsetY);
  1431. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1432. context->state->shadowOffsetY = Nan::To<double>(value).FromMaybe(0);
  1433. }
  1434. /*
  1435. * Get shadow blur.
  1436. */
  1437. NAN_GETTER(Context2d::GetShadowBlur) {
  1438. CHECK_RECEIVER(Context2d.GetShadowBlur);
  1439. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1440. info.GetReturnValue().Set(Nan::New<Number>(context->state->shadowBlur));
  1441. }
  1442. /*
  1443. * Set shadow blur.
  1444. */
  1445. NAN_SETTER(Context2d::SetShadowBlur) {
  1446. CHECK_RECEIVER(Context2d.SetShadowBlur);
  1447. int n = Nan::To<double>(value).FromMaybe(0);
  1448. if (n >= 0) {
  1449. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1450. context->state->shadowBlur = n;
  1451. }
  1452. }
  1453. /*
  1454. * Get current antialiasing setting.
  1455. */
  1456. NAN_GETTER(Context2d::GetAntiAlias) {
  1457. CHECK_RECEIVER(Context2d.GetAntiAlias);
  1458. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1459. const char *aa;
  1460. switch (cairo_get_antialias(context->context())) {
  1461. case CAIRO_ANTIALIAS_NONE: aa = "none"; break;
  1462. case CAIRO_ANTIALIAS_GRAY: aa = "gray"; break;
  1463. case CAIRO_ANTIALIAS_SUBPIXEL: aa = "subpixel"; break;
  1464. default: aa = "default";
  1465. }
  1466. info.GetReturnValue().Set(Nan::New(aa).ToLocalChecked());
  1467. }
  1468. /*
  1469. * Set antialiasing.
  1470. */
  1471. NAN_SETTER(Context2d::SetAntiAlias) {
  1472. CHECK_RECEIVER(Context2d.SetAntiAlias);
  1473. Nan::Utf8String str(Nan::To<String>(value).ToLocalChecked());
  1474. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1475. cairo_t *ctx = context->context();
  1476. cairo_antialias_t a;
  1477. if (0 == strcmp("none", *str)) {
  1478. a = CAIRO_ANTIALIAS_NONE;
  1479. } else if (0 == strcmp("default", *str)) {
  1480. a = CAIRO_ANTIALIAS_DEFAULT;
  1481. } else if (0 == strcmp("gray", *str)) {
  1482. a = CAIRO_ANTIALIAS_GRAY;
  1483. } else if (0 == strcmp("subpixel", *str)) {
  1484. a = CAIRO_ANTIALIAS_SUBPIXEL;
  1485. } else {
  1486. a = cairo_get_antialias(ctx);
  1487. }
  1488. cairo_set_antialias(ctx, a);
  1489. }
  1490. /*
  1491. * Get text drawing mode.
  1492. */
  1493. NAN_GETTER(Context2d::GetTextDrawingMode) {
  1494. CHECK_RECEIVER(Context2d.GetTextDrawingMode);
  1495. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1496. const char *mode;
  1497. if (context->state->textDrawingMode == TEXT_DRAW_PATHS) {
  1498. mode = "path";
  1499. } else if (context->state->textDrawingMode == TEXT_DRAW_GLYPHS) {
  1500. mode = "glyph";
  1501. } else {
  1502. mode = "unknown";
  1503. }
  1504. info.GetReturnValue().Set(Nan::New(mode).ToLocalChecked());
  1505. }
  1506. /*
  1507. * Set text drawing mode.
  1508. */
  1509. NAN_SETTER(Context2d::SetTextDrawingMode) {
  1510. CHECK_RECEIVER(Context2d.SetTextDrawingMode);
  1511. Nan::Utf8String str(Nan::To<String>(value).ToLocalChecked());
  1512. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1513. if (0 == strcmp("path", *str)) {
  1514. context->state->textDrawingMode = TEXT_DRAW_PATHS;
  1515. } else if (0 == strcmp("glyph", *str)) {
  1516. context->state->textDrawingMode = TEXT_DRAW_GLYPHS;
  1517. }
  1518. }
  1519. /*
  1520. * Get filter.
  1521. */
  1522. NAN_GETTER(Context2d::GetQuality) {
  1523. CHECK_RECEIVER(Context2d.GetQuality);
  1524. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1525. const char *filter;
  1526. switch (cairo_pattern_get_filter(cairo_get_source(context->context()))) {
  1527. case CAIRO_FILTER_FAST: filter = "fast"; break;
  1528. case CAIRO_FILTER_BEST: filter = "best"; break;
  1529. case CAIRO_FILTER_NEAREST: filter = "nearest"; break;
  1530. case CAIRO_FILTER_BILINEAR: filter = "bilinear"; break;
  1531. default: filter = "good";
  1532. }
  1533. info.GetReturnValue().Set(Nan::New(filter).ToLocalChecked());
  1534. }
  1535. /*
  1536. * Set filter.
  1537. */
  1538. NAN_SETTER(Context2d::SetQuality) {
  1539. CHECK_RECEIVER(Context2d.SetQuality);
  1540. Nan::Utf8String str(Nan::To<String>(value).ToLocalChecked());
  1541. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1542. cairo_filter_t filter;
  1543. if (0 == strcmp("fast", *str)) {
  1544. filter = CAIRO_FILTER_FAST;
  1545. } else if (0 == strcmp("best", *str)) {
  1546. filter = CAIRO_FILTER_BEST;
  1547. } else if (0 == strcmp("nearest", *str)) {
  1548. filter = CAIRO_FILTER_NEAREST;
  1549. } else if (0 == strcmp("bilinear", *str)) {
  1550. filter = CAIRO_FILTER_BILINEAR;
  1551. } else {
  1552. filter = CAIRO_FILTER_GOOD;
  1553. }
  1554. cairo_pattern_set_filter(cairo_get_source(context->context()), filter);
  1555. }
  1556. /*
  1557. * Helper for get current transform matrix
  1558. */
  1559. Local<Object>
  1560. get_current_transform(Context2d *context) {
  1561. Isolate *iso = Isolate::GetCurrent();
  1562. Local<Float64Array> arr = Float64Array::New(ArrayBuffer::New(iso, 48), 0, 6);
  1563. Nan::TypedArrayContents<double> dest(arr);
  1564. cairo_matrix_t matrix;
  1565. cairo_get_matrix(context->context(), &matrix);
  1566. (*dest)[0] = matrix.xx;
  1567. (*dest)[1] = matrix.yx;
  1568. (*dest)[2] = matrix.xy;
  1569. (*dest)[3] = matrix.yy;
  1570. (*dest)[4] = matrix.x0;
  1571. (*dest)[5] = matrix.y0;
  1572. const int argc = 1;
  1573. Local<Value> argv[argc] = { arr };
  1574. return Nan::NewInstance(context->_DOMMatrix.Get(iso), argc, argv).ToLocalChecked();
  1575. }
  1576. /*
  1577. * Helper for get/set transform.
  1578. */
  1579. void parse_matrix_from_object(cairo_matrix_t &matrix, Local<Object> mat) {
  1580. cairo_matrix_init(&matrix,
  1581. Nan::To<double>(Nan::Get(mat, Nan::New("a").ToLocalChecked()).ToLocalChecked()).FromMaybe(0),
  1582. Nan::To<double>(Nan::Get(mat, Nan::New("b").ToLocalChecked()).ToLocalChecked()).FromMaybe(0),
  1583. Nan::To<double>(Nan::Get(mat, Nan::New("c").ToLocalChecked()).ToLocalChecked()).FromMaybe(0),
  1584. Nan::To<double>(Nan::Get(mat, Nan::New("d").ToLocalChecked()).ToLocalChecked()).FromMaybe(0),
  1585. Nan::To<double>(Nan::Get(mat, Nan::New("e").ToLocalChecked()).ToLocalChecked()).FromMaybe(0),
  1586. Nan::To<double>(Nan::Get(mat, Nan::New("f").ToLocalChecked()).ToLocalChecked()).FromMaybe(0)
  1587. );
  1588. }
  1589. /*
  1590. * Get current transform.
  1591. */
  1592. NAN_GETTER(Context2d::GetCurrentTransform) {
  1593. CHECK_RECEIVER(Context2d.GetCurrentTransform);
  1594. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1595. Local<Object> instance = get_current_transform(context);
  1596. info.GetReturnValue().Set(instance);
  1597. }
  1598. /*
  1599. * Set current transform.
  1600. */
  1601. NAN_SETTER(Context2d::SetCurrentTransform) {
  1602. CHECK_RECEIVER(Context2d.SetCurrentTransform);
  1603. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1604. Local<Context> ctx = Nan::GetCurrentContext();
  1605. Local<Object> mat = Nan::To<Object>(value).ToLocalChecked();
  1606. #if NODE_MAJOR_VERSION >= 8
  1607. if (!mat->InstanceOf(ctx, _DOMMatrix.Get(Isolate::GetCurrent())).ToChecked()) {
  1608. return Nan::ThrowTypeError("Expected DOMMatrix");
  1609. }
  1610. #endif
  1611. cairo_matrix_t matrix;
  1612. parse_matrix_from_object(matrix, mat);
  1613. cairo_transform(context->context(), &matrix);
  1614. }
  1615. /*
  1616. * Get current fill style.
  1617. */
  1618. NAN_GETTER(Context2d::GetFillStyle) {
  1619. CHECK_RECEIVER(Context2d.GetFillStyle);
  1620. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1621. Isolate *iso = Isolate::GetCurrent();
  1622. Local<Value> style;
  1623. if (context->_fillStyle.IsEmpty())
  1624. style = context->_getFillColor();
  1625. else
  1626. style = context->_fillStyle.Get(iso);
  1627. info.GetReturnValue().Set(style);
  1628. }
  1629. /*
  1630. * Set current fill style.
  1631. */
  1632. NAN_SETTER(Context2d::SetFillStyle) {
  1633. CHECK_RECEIVER(Context2d.SetFillStyle);
  1634. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1635. if (value->IsString()) {
  1636. MaybeLocal<String> mstr = Nan::To<String>(value);
  1637. if (mstr.IsEmpty()) return;
  1638. Local<String> str = mstr.ToLocalChecked();
  1639. context->_fillStyle.Reset();
  1640. context->_setFillColor(str);
  1641. } else if (value->IsObject()) {
  1642. Local<Object> obj = Nan::To<Object>(value).ToLocalChecked();
  1643. if (Nan::New(Gradient::constructor)->HasInstance(obj)) {
  1644. context->_fillStyle.Reset(value);
  1645. Gradient *grad = Nan::ObjectWrap::Unwrap<Gradient>(obj);
  1646. context->state->fillGradient = grad->pattern();
  1647. } else if (Nan::New(Pattern::constructor)->HasInstance(obj)) {
  1648. context->_fillStyle.Reset(value);
  1649. Pattern *pattern = Nan::ObjectWrap::Unwrap<Pattern>(obj);
  1650. context->state->fillPattern = pattern->pattern();
  1651. }
  1652. }
  1653. }
  1654. /*
  1655. * Get current stroke style.
  1656. */
  1657. NAN_GETTER(Context2d::GetStrokeStyle) {
  1658. CHECK_RECEIVER(Context2d.GetStrokeStyle);
  1659. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1660. Local<Value> style;
  1661. if (context->_strokeStyle.IsEmpty())
  1662. style = context->_getStrokeColor();
  1663. else
  1664. style = context->_strokeStyle.Get(Isolate::GetCurrent());
  1665. info.GetReturnValue().Set(style);
  1666. }
  1667. /*
  1668. * Set current stroke style.
  1669. */
  1670. NAN_SETTER(Context2d::SetStrokeStyle) {
  1671. CHECK_RECEIVER(Context2d.SetStrokeStyle);
  1672. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1673. if (value->IsString()) {
  1674. MaybeLocal<String> mstr = Nan::To<String>(value);
  1675. if (mstr.IsEmpty()) return;
  1676. Local<String> str = mstr.ToLocalChecked();
  1677. context->_strokeStyle.Reset();
  1678. context->_setStrokeColor(str);
  1679. } else if (value->IsObject()) {
  1680. Local<Object> obj = Nan::To<Object>(value).ToLocalChecked();
  1681. if (Nan::New(Gradient::constructor)->HasInstance(obj)) {
  1682. context->_strokeStyle.Reset(value);
  1683. Gradient *grad = Nan::ObjectWrap::Unwrap<Gradient>(obj);
  1684. context->state->strokeGradient = grad->pattern();
  1685. } else if (Nan::New(Pattern::constructor)->HasInstance(obj)) {
  1686. context->_strokeStyle.Reset(value);
  1687. Pattern *pattern = Nan::ObjectWrap::Unwrap<Pattern>(obj);
  1688. context->state->strokePattern = pattern->pattern();
  1689. }
  1690. }
  1691. }
  1692. /*
  1693. * Get miter limit.
  1694. */
  1695. NAN_GETTER(Context2d::GetMiterLimit) {
  1696. CHECK_RECEIVER(Context2d.GetMiterLimit);
  1697. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1698. info.GetReturnValue().Set(Nan::New<Number>(cairo_get_miter_limit(context->context())));
  1699. }
  1700. /*
  1701. * Set miter limit.
  1702. */
  1703. NAN_SETTER(Context2d::SetMiterLimit) {
  1704. CHECK_RECEIVER(Context2d.SetMiterLimit);
  1705. double n = Nan::To<double>(value).FromMaybe(0);
  1706. if (n > 0) {
  1707. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1708. cairo_set_miter_limit(context->context(), n);
  1709. }
  1710. }
  1711. /*
  1712. * Get line width.
  1713. */
  1714. NAN_GETTER(Context2d::GetLineWidth) {
  1715. CHECK_RECEIVER(Context2d.GetLineWidth);
  1716. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1717. info.GetReturnValue().Set(Nan::New<Number>(cairo_get_line_width(context->context())));
  1718. }
  1719. /*
  1720. * Set line width.
  1721. */
  1722. NAN_SETTER(Context2d::SetLineWidth) {
  1723. CHECK_RECEIVER(Context2d.SetLineWidth);
  1724. double n = Nan::To<double>(value).FromMaybe(0);
  1725. if (n > 0 && n != std::numeric_limits<double>::infinity()) {
  1726. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1727. cairo_set_line_width(context->context(), n);
  1728. }
  1729. }
  1730. /*
  1731. * Get line join.
  1732. */
  1733. NAN_GETTER(Context2d::GetLineJoin) {
  1734. CHECK_RECEIVER(Context2d.GetLineJoin);
  1735. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1736. const char *join;
  1737. switch (cairo_get_line_join(context->context())) {
  1738. case CAIRO_LINE_JOIN_BEVEL: join = "bevel"; break;
  1739. case CAIRO_LINE_JOIN_ROUND: join = "round"; break;
  1740. default: join = "miter";
  1741. }
  1742. info.GetReturnValue().Set(Nan::New(join).ToLocalChecked());
  1743. }
  1744. /*
  1745. * Set line join.
  1746. */
  1747. NAN_SETTER(Context2d::SetLineJoin) {
  1748. CHECK_RECEIVER(Context2d.SetLineJoin);
  1749. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1750. cairo_t *ctx = context->context();
  1751. Nan::Utf8String type(Nan::To<String>(value).ToLocalChecked());
  1752. if (0 == strcmp("round", *type)) {
  1753. cairo_set_line_join(ctx, CAIRO_LINE_JOIN_ROUND);
  1754. } else if (0 == strcmp("bevel", *type)) {
  1755. cairo_set_line_join(ctx, CAIRO_LINE_JOIN_BEVEL);
  1756. } else {
  1757. cairo_set_line_join(ctx, CAIRO_LINE_JOIN_MITER);
  1758. }
  1759. }
  1760. /*
  1761. * Get line cap.
  1762. */
  1763. NAN_GETTER(Context2d::GetLineCap) {
  1764. CHECK_RECEIVER(Context2d.GetLineCap);
  1765. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1766. const char *cap;
  1767. switch (cairo_get_line_cap(context->context())) {
  1768. case CAIRO_LINE_CAP_ROUND: cap = "round"; break;
  1769. case CAIRO_LINE_CAP_SQUARE: cap = "square"; break;
  1770. default: cap = "butt";
  1771. }
  1772. info.GetReturnValue().Set(Nan::New(cap).ToLocalChecked());
  1773. }
  1774. /*
  1775. * Set line cap.
  1776. */
  1777. NAN_SETTER(Context2d::SetLineCap) {
  1778. CHECK_RECEIVER(Context2d.SetLineCap);
  1779. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1780. cairo_t *ctx = context->context();
  1781. Nan::Utf8String type(Nan::To<String>(value).ToLocalChecked());
  1782. if (0 == strcmp("round", *type)) {
  1783. cairo_set_line_cap(ctx, CAIRO_LINE_CAP_ROUND);
  1784. } else if (0 == strcmp("square", *type)) {
  1785. cairo_set_line_cap(ctx, CAIRO_LINE_CAP_SQUARE);
  1786. } else {
  1787. cairo_set_line_cap(ctx, CAIRO_LINE_CAP_BUTT);
  1788. }
  1789. }
  1790. /*
  1791. * Check if the given point is within the current path.
  1792. */
  1793. NAN_METHOD(Context2d::IsPointInPath) {
  1794. if (info[0]->IsNumber() && info[1]->IsNumber()) {
  1795. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1796. cairo_t *ctx = context->context();
  1797. double x = Nan::To<double>(info[0]).FromMaybe(0)
  1798. , y = Nan::To<double>(info[1]).FromMaybe(0);
  1799. context->setFillRule(info[2]);
  1800. info.GetReturnValue().Set(Nan::New<Boolean>(cairo_in_fill(ctx, x, y) || cairo_in_stroke(ctx, x, y)));
  1801. return;
  1802. }
  1803. info.GetReturnValue().Set(Nan::False());
  1804. }
  1805. /*
  1806. * Set shadow color.
  1807. */
  1808. NAN_SETTER(Context2d::SetShadowColor) {
  1809. CHECK_RECEIVER(Context2d.SetShadowColor);
  1810. short ok;
  1811. Nan::Utf8String str(Nan::To<String>(value).ToLocalChecked());
  1812. uint32_t rgba = rgba_from_string(*str, &ok);
  1813. if (ok) {
  1814. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1815. context->state->shadow = rgba_create(rgba);
  1816. }
  1817. }
  1818. /*
  1819. * Get shadow color.
  1820. */
  1821. NAN_GETTER(Context2d::GetShadowColor) {
  1822. CHECK_RECEIVER(Context2d.GetShadowColor);
  1823. char buf[64];
  1824. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1825. rgba_to_string(context->state->shadow, buf, sizeof(buf));
  1826. info.GetReturnValue().Set(Nan::New<String>(buf).ToLocalChecked());
  1827. }
  1828. /*
  1829. * Set fill color, used internally for fillStyle=
  1830. */
  1831. void Context2d::_setFillColor(Local<Value> arg) {
  1832. short ok;
  1833. Nan::Utf8String str(arg);
  1834. uint32_t rgba = rgba_from_string(*str, &ok);
  1835. if (!ok) return;
  1836. state->fillPattern = state->fillGradient = NULL;
  1837. state->fill = rgba_create(rgba);
  1838. }
  1839. /*
  1840. * Get fill color.
  1841. */
  1842. Local<Value> Context2d::_getFillColor() {
  1843. char buf[64];
  1844. rgba_to_string(state->fill, buf, sizeof(buf));
  1845. return Nan::New<String>(buf).ToLocalChecked();
  1846. }
  1847. /*
  1848. * Set stroke color, used internally for strokeStyle=
  1849. */
  1850. void Context2d::_setStrokeColor(Local<Value> arg) {
  1851. short ok;
  1852. Nan::Utf8String str(arg);
  1853. uint32_t rgba = rgba_from_string(*str, &ok);
  1854. if (!ok) return;
  1855. state->strokePattern = state->strokeGradient = NULL;
  1856. state->stroke = rgba_create(rgba);
  1857. }
  1858. /*
  1859. * Get stroke color.
  1860. */
  1861. Local<Value> Context2d::_getStrokeColor() {
  1862. char buf[64];
  1863. rgba_to_string(state->stroke, buf, sizeof(buf));
  1864. return Nan::New<String>(buf).ToLocalChecked();
  1865. }
  1866. NAN_METHOD(Context2d::CreatePattern) {
  1867. Local<Value> image = info[0];
  1868. Local<Value> repetition = info[1];
  1869. if (!Nan::To<bool>(repetition).FromMaybe(false))
  1870. repetition = Nan::New("repeat").ToLocalChecked();
  1871. const int argc = 2;
  1872. Local<Value> argv[argc] = { image, repetition };
  1873. Local<Function> ctor = Nan::GetFunction(Nan::New(Pattern::constructor)).ToLocalChecked();
  1874. Local<Object> instance = Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
  1875. info.GetReturnValue().Set(instance);
  1876. }
  1877. NAN_METHOD(Context2d::CreateLinearGradient) {
  1878. const int argc = 4;
  1879. Local<Value> argv[argc] = { info[0], info[1], info[2], info[3] };
  1880. Local<Function> ctor = Nan::GetFunction(Nan::New(Gradient::constructor)).ToLocalChecked();
  1881. Local<Object> instance = Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
  1882. info.GetReturnValue().Set(instance);
  1883. }
  1884. NAN_METHOD(Context2d::CreateRadialGradient) {
  1885. const int argc = 6;
  1886. Local<Value> argv[argc] = { info[0], info[1], info[2], info[3], info[4], info[5] };
  1887. Local<Function> ctor = Nan::GetFunction(Nan::New(Gradient::constructor)).ToLocalChecked();
  1888. Local<Object> instance = Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
  1889. info.GetReturnValue().Set(instance);
  1890. }
  1891. /*
  1892. * Bezier curve.
  1893. */
  1894. NAN_METHOD(Context2d::BezierCurveTo) {
  1895. double args[6];
  1896. if(!checkArgs(info, args, 6))
  1897. return;
  1898. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1899. cairo_curve_to(context->context()
  1900. , args[0]
  1901. , args[1]
  1902. , args[2]
  1903. , args[3]
  1904. , args[4]
  1905. , args[5]);
  1906. }
  1907. /*
  1908. * Quadratic curve approximation from libsvg-cairo.
  1909. */
  1910. NAN_METHOD(Context2d::QuadraticCurveTo) {
  1911. double args[4];
  1912. if(!checkArgs(info, args, 4))
  1913. return;
  1914. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1915. cairo_t *ctx = context->context();
  1916. double x, y
  1917. , x1 = args[0]
  1918. , y1 = args[1]
  1919. , x2 = args[2]
  1920. , y2 = args[3];
  1921. cairo_get_current_point(ctx, &x, &y);
  1922. if (0 == x && 0 == y) {
  1923. x = x1;
  1924. y = y1;
  1925. }
  1926. cairo_curve_to(ctx
  1927. , x + 2.0 / 3.0 * (x1 - x), y + 2.0 / 3.0 * (y1 - y)
  1928. , x2 + 2.0 / 3.0 * (x1 - x2), y2 + 2.0 / 3.0 * (y1 - y2)
  1929. , x2
  1930. , y2);
  1931. }
  1932. /*
  1933. * Save state.
  1934. */
  1935. NAN_METHOD(Context2d::Save) {
  1936. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1937. context->save();
  1938. }
  1939. /*
  1940. * Restore state.
  1941. */
  1942. NAN_METHOD(Context2d::Restore) {
  1943. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1944. context->restore();
  1945. }
  1946. /*
  1947. * Creates a new subpath.
  1948. */
  1949. NAN_METHOD(Context2d::BeginPath) {
  1950. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1951. cairo_new_path(context->context());
  1952. }
  1953. /*
  1954. * Marks the subpath as closed.
  1955. */
  1956. NAN_METHOD(Context2d::ClosePath) {
  1957. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1958. cairo_close_path(context->context());
  1959. }
  1960. /*
  1961. * Rotate transformation.
  1962. */
  1963. NAN_METHOD(Context2d::Rotate) {
  1964. double args[1];
  1965. if(!checkArgs(info, args, 1))
  1966. return;
  1967. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1968. cairo_rotate(context->context(), args[0]);
  1969. }
  1970. /*
  1971. * Modify the CTM.
  1972. */
  1973. NAN_METHOD(Context2d::Transform) {
  1974. double args[6];
  1975. if(!checkArgs(info, args, 6))
  1976. return;
  1977. cairo_matrix_t matrix;
  1978. cairo_matrix_init(&matrix
  1979. , args[0]
  1980. , args[1]
  1981. , args[2]
  1982. , args[3]
  1983. , args[4]
  1984. , args[5]);
  1985. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1986. cairo_transform(context->context(), &matrix);
  1987. }
  1988. /*
  1989. * Get the CTM
  1990. */
  1991. NAN_METHOD(Context2d::GetTransform) {
  1992. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  1993. Local<Object> instance = get_current_transform(context);
  1994. info.GetReturnValue().Set(instance);
  1995. }
  1996. /*
  1997. * Reset the CTM, used internally by setTransform().
  1998. */
  1999. NAN_METHOD(Context2d::ResetTransform) {
  2000. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2001. cairo_identity_matrix(context->context());
  2002. }
  2003. /*
  2004. * Reset transform matrix to identity, then apply the given args.
  2005. */
  2006. NAN_METHOD(Context2d::SetTransform) {
  2007. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2008. if (info.Length() == 1) {
  2009. Local<Object> mat = Nan::To<Object>(info[0]).ToLocalChecked();
  2010. #if NODE_MAJOR_VERSION >= 8
  2011. Local<Context> ctx = Nan::GetCurrentContext();
  2012. if (!mat->InstanceOf(ctx, _DOMMatrix.Get(Isolate::GetCurrent())).ToChecked()) {
  2013. return Nan::ThrowTypeError("Expected DOMMatrix");
  2014. }
  2015. #endif
  2016. cairo_matrix_t matrix;
  2017. parse_matrix_from_object(matrix, mat);
  2018. cairo_set_matrix(context->context(), &matrix);
  2019. } else {
  2020. cairo_identity_matrix(context->context());
  2021. Context2d::Transform(info);
  2022. }
  2023. }
  2024. /*
  2025. * Translate transformation.
  2026. */
  2027. NAN_METHOD(Context2d::Translate) {
  2028. double args[2];
  2029. if(!checkArgs(info, args, 2))
  2030. return;
  2031. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2032. cairo_translate(context->context(), args[0], args[1]);
  2033. }
  2034. /*
  2035. * Scale transformation.
  2036. */
  2037. NAN_METHOD(Context2d::Scale) {
  2038. double args[2];
  2039. if(!checkArgs(info, args, 2))
  2040. return;
  2041. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2042. cairo_scale(context->context(), args[0], args[1]);
  2043. }
  2044. /*
  2045. * Use path as clipping region.
  2046. */
  2047. NAN_METHOD(Context2d::Clip) {
  2048. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2049. context->setFillRule(info[0]);
  2050. cairo_t *ctx = context->context();
  2051. cairo_clip_preserve(ctx);
  2052. }
  2053. /*
  2054. * Fill the path.
  2055. */
  2056. NAN_METHOD(Context2d::Fill) {
  2057. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2058. context->setFillRule(info[0]);
  2059. context->fill(true);
  2060. }
  2061. /*
  2062. * Stroke the path.
  2063. */
  2064. NAN_METHOD(Context2d::Stroke) {
  2065. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2066. context->stroke(true);
  2067. }
  2068. /*
  2069. * Helper for fillText/strokeText
  2070. */
  2071. double
  2072. get_text_scale(PangoLayout *layout, double maxWidth) {
  2073. PangoRectangle logical_rect;
  2074. pango_layout_get_pixel_extents(layout, NULL, &logical_rect);
  2075. if (logical_rect.width > maxWidth) {
  2076. return maxWidth / logical_rect.width;
  2077. } else {
  2078. return 1.0;
  2079. }
  2080. }
  2081. void
  2082. paintText(const Nan::FunctionCallbackInfo<Value> &info, bool stroke) {
  2083. int argsNum = info.Length() >= 4 ? 3 : 2;
  2084. if (argsNum == 3 && info[3]->IsUndefined())
  2085. argsNum = 2;
  2086. double args[3];
  2087. if(!checkArgs(info, args, argsNum, 1))
  2088. return;
  2089. Nan::Utf8String str(Nan::To<String>(info[0]).ToLocalChecked());
  2090. double x = args[0];
  2091. double y = args[1];
  2092. double scaled_by = 1;
  2093. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2094. PangoLayout *layout = context->layout();
  2095. pango_layout_set_text(layout, *str, -1);
  2096. pango_cairo_update_layout(context->context(), layout);
  2097. if (argsNum == 3) {
  2098. scaled_by = get_text_scale(layout, args[2]);
  2099. cairo_save(context->context());
  2100. cairo_scale(context->context(), scaled_by, 1);
  2101. }
  2102. context->savePath();
  2103. if (context->state->textDrawingMode == TEXT_DRAW_GLYPHS) {
  2104. if (stroke == true) { context->stroke(); } else { context->fill(); }
  2105. context->setTextPath(x / scaled_by, y);
  2106. } else if (context->state->textDrawingMode == TEXT_DRAW_PATHS) {
  2107. context->setTextPath(x / scaled_by, y);
  2108. if (stroke == true) { context->stroke(); } else { context->fill(); }
  2109. }
  2110. context->restorePath();
  2111. if (argsNum == 3) {
  2112. cairo_restore(context->context());
  2113. }
  2114. }
  2115. /*
  2116. * Fill text at (x, y).
  2117. */
  2118. NAN_METHOD(Context2d::FillText) {
  2119. paintText(info, false);
  2120. }
  2121. /*
  2122. * Stroke text at (x ,y).
  2123. */
  2124. NAN_METHOD(Context2d::StrokeText) {
  2125. paintText(info, true);
  2126. }
  2127. /*
  2128. * Gets the baseline adjustment in device pixels
  2129. */
  2130. inline double getBaselineAdjustment(PangoLayout* layout, short baseline) {
  2131. PangoRectangle logical_rect;
  2132. pango_layout_line_get_extents(pango_layout_get_line(layout, 0), NULL, &logical_rect);
  2133. double scale = 1.0 / PANGO_SCALE;
  2134. double ascent = scale * pango_layout_get_baseline(layout);
  2135. double descent = scale * logical_rect.height - ascent;
  2136. switch (baseline) {
  2137. case TEXT_BASELINE_ALPHABETIC:
  2138. return ascent;
  2139. case TEXT_BASELINE_MIDDLE:
  2140. return (ascent + descent) / 2.0;
  2141. case TEXT_BASELINE_BOTTOM:
  2142. return ascent + descent;
  2143. default:
  2144. return 0;
  2145. }
  2146. }
  2147. /*
  2148. * Set text path for the string in the layout at (x, y).
  2149. * This function is called by paintText and won't behave correctly
  2150. * if is not called from there.
  2151. * it needs pango_layout_set_text and pango_cairo_update_layout to be called before
  2152. */
  2153. void
  2154. Context2d::setTextPath(double x, double y) {
  2155. PangoRectangle logical_rect;
  2156. switch (state->textAlignment) {
  2157. case TEXT_ALIGNMENT_CENTER:
  2158. pango_layout_get_pixel_extents(_layout, NULL, &logical_rect);
  2159. x -= logical_rect.width / 2;
  2160. break;
  2161. case TEXT_ALIGNMENT_END:
  2162. case TEXT_ALIGNMENT_RIGHT:
  2163. pango_layout_get_pixel_extents(_layout, NULL, &logical_rect);
  2164. x -= logical_rect.width;
  2165. break;
  2166. }
  2167. y -= getBaselineAdjustment(_layout, state->textBaseline);
  2168. cairo_move_to(_context, x, y);
  2169. if (state->textDrawingMode == TEXT_DRAW_PATHS) {
  2170. pango_cairo_layout_path(_context, _layout);
  2171. } else if (state->textDrawingMode == TEXT_DRAW_GLYPHS) {
  2172. pango_cairo_show_layout(_context, _layout);
  2173. }
  2174. }
  2175. /*
  2176. * Adds a point to the current subpath.
  2177. */
  2178. NAN_METHOD(Context2d::LineTo) {
  2179. double args[2];
  2180. if(!checkArgs(info, args, 2))
  2181. return;
  2182. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2183. cairo_line_to(context->context(), args[0], args[1]);
  2184. }
  2185. /*
  2186. * Creates a new subpath at the given point.
  2187. */
  2188. NAN_METHOD(Context2d::MoveTo) {
  2189. double args[2];
  2190. if(!checkArgs(info, args, 2))
  2191. return;
  2192. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2193. cairo_move_to(context->context(), args[0], args[1]);
  2194. }
  2195. /*
  2196. * Get font.
  2197. */
  2198. NAN_GETTER(Context2d::GetFont) {
  2199. CHECK_RECEIVER(Context2d.GetFont);
  2200. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2201. Isolate *iso = Isolate::GetCurrent();
  2202. Local<Value> font;
  2203. if (context->_font.IsEmpty())
  2204. font = Nan::New("10px sans-serif").ToLocalChecked();
  2205. else
  2206. font = context->_font.Get(iso);
  2207. info.GetReturnValue().Set(font);
  2208. }
  2209. /*
  2210. * Set font:
  2211. * - weight
  2212. * - style
  2213. * - size
  2214. * - unit
  2215. * - family
  2216. */
  2217. NAN_SETTER(Context2d::SetFont) {
  2218. CHECK_RECEIVER(Context2d.SetFont);
  2219. if (!value->IsString()) return;
  2220. Isolate *iso = Isolate::GetCurrent();
  2221. Local<Context> ctx = Nan::GetCurrentContext();
  2222. Local<String> str = Nan::To<String>(value).ToLocalChecked();
  2223. if (!str->Length()) return;
  2224. const int argc = 1;
  2225. Local<Value> argv[argc] = { value };
  2226. Local<Value> mparsed = Nan::Call(_parseFont.Get(iso), ctx->Global(), argc, argv).ToLocalChecked();
  2227. // parseFont returns undefined for invalid CSS font strings
  2228. if (mparsed->IsUndefined()) return;
  2229. Local<Object> font = Nan::To<Object>(mparsed).ToLocalChecked();
  2230. Nan::Utf8String weight(Nan::Get(font, Nan::New("weight").ToLocalChecked()).ToLocalChecked());
  2231. Nan::Utf8String style(Nan::Get(font, Nan::New("style").ToLocalChecked()).ToLocalChecked());
  2232. double size = Nan::To<double>(Nan::Get(font, Nan::New("size").ToLocalChecked()).ToLocalChecked()).FromMaybe(0);
  2233. Nan::Utf8String unit(Nan::Get(font, Nan::New("unit").ToLocalChecked()).ToLocalChecked());
  2234. Nan::Utf8String family(Nan::Get(font, Nan::New("family").ToLocalChecked()).ToLocalChecked());
  2235. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2236. PangoFontDescription *desc = pango_font_description_copy(context->state->fontDescription);
  2237. pango_font_description_free(context->state->fontDescription);
  2238. pango_font_description_set_style(desc, Canvas::GetStyleFromCSSString(*style));
  2239. pango_font_description_set_weight(desc, Canvas::GetWeightFromCSSString(*weight));
  2240. if (strlen(*family) > 0) {
  2241. // See #1643 - Pango understands "sans" whereas CSS uses "sans-serif"
  2242. std::string s1(*family);
  2243. std::string s2("sans-serif");
  2244. if (streq_casein(s1, s2)) {
  2245. pango_font_description_set_family(desc, "sans");
  2246. } else {
  2247. pango_font_description_set_family(desc, *family);
  2248. }
  2249. }
  2250. PangoFontDescription *sys_desc = Canvas::ResolveFontDescription(desc);
  2251. pango_font_description_free(desc);
  2252. if (size > 0) pango_font_description_set_absolute_size(sys_desc, size * PANGO_SCALE);
  2253. context->state->fontDescription = sys_desc;
  2254. pango_layout_set_font_description(context->_layout, sys_desc);
  2255. context->_font.Reset(value);
  2256. }
  2257. /*
  2258. * Get text baseline.
  2259. */
  2260. NAN_GETTER(Context2d::GetTextBaseline) {
  2261. CHECK_RECEIVER(Context2d.GetTextBaseline);
  2262. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2263. const char* baseline;
  2264. switch (context->state->textBaseline) {
  2265. default:
  2266. case TEXT_BASELINE_ALPHABETIC: baseline = "alphabetic"; break;
  2267. case TEXT_BASELINE_TOP: baseline = "top"; break;
  2268. case TEXT_BASELINE_BOTTOM: baseline = "bottom"; break;
  2269. case TEXT_BASELINE_MIDDLE: baseline = "middle"; break;
  2270. case TEXT_BASELINE_IDEOGRAPHIC: baseline = "ideographic"; break;
  2271. case TEXT_BASELINE_HANGING: baseline = "hanging"; break;
  2272. }
  2273. info.GetReturnValue().Set(Nan::New(baseline).ToLocalChecked());
  2274. }
  2275. /*
  2276. * Set text baseline.
  2277. */
  2278. NAN_SETTER(Context2d::SetTextBaseline) {
  2279. CHECK_RECEIVER(Context2d.SetTextBaseline);
  2280. if (!value->IsString()) return;
  2281. Nan::Utf8String opStr(Nan::To<String>(value).ToLocalChecked());
  2282. const std::map<std::string, text_baseline_t> modes = {
  2283. {"alphabetic", TEXT_BASELINE_ALPHABETIC},
  2284. {"top", TEXT_BASELINE_TOP},
  2285. {"bottom", TEXT_BASELINE_BOTTOM},
  2286. {"middle", TEXT_BASELINE_MIDDLE},
  2287. {"ideographic", TEXT_BASELINE_IDEOGRAPHIC},
  2288. {"hanging", TEXT_BASELINE_HANGING}
  2289. };
  2290. auto op = modes.find(*opStr);
  2291. if (op == modes.end()) return;
  2292. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2293. context->state->textBaseline = op->second;
  2294. }
  2295. /*
  2296. * Get text align.
  2297. */
  2298. NAN_GETTER(Context2d::GetTextAlign) {
  2299. CHECK_RECEIVER(Context2d.GetTextAlign);
  2300. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2301. const char* align;
  2302. switch (context->state->textAlignment) {
  2303. default:
  2304. // TODO the default is supposed to be "start"
  2305. case TEXT_ALIGNMENT_LEFT: align = "left"; break;
  2306. case TEXT_ALIGNMENT_START: align = "start"; break;
  2307. case TEXT_ALIGNMENT_CENTER: align = "center"; break;
  2308. case TEXT_ALIGNMENT_RIGHT: align = "right"; break;
  2309. case TEXT_ALIGNMENT_END: align = "end"; break;
  2310. }
  2311. info.GetReturnValue().Set(Nan::New(align).ToLocalChecked());
  2312. }
  2313. /*
  2314. * Set text align.
  2315. */
  2316. NAN_SETTER(Context2d::SetTextAlign) {
  2317. CHECK_RECEIVER(Context2d.SetTextAlign);
  2318. if (!value->IsString()) return;
  2319. Nan::Utf8String opStr(Nan::To<String>(value).ToLocalChecked());
  2320. const std::map<std::string, text_align_t> modes = {
  2321. {"center", TEXT_ALIGNMENT_CENTER},
  2322. {"left", TEXT_ALIGNMENT_LEFT},
  2323. {"start", TEXT_ALIGNMENT_START},
  2324. {"right", TEXT_ALIGNMENT_RIGHT},
  2325. {"end", TEXT_ALIGNMENT_END}
  2326. };
  2327. auto op = modes.find(*opStr);
  2328. if (op == modes.end()) return;
  2329. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2330. context->state->textAlignment = op->second;
  2331. }
  2332. /*
  2333. * Return the given text extents.
  2334. * TODO: Support for:
  2335. * hangingBaseline, ideographicBaseline,
  2336. * fontBoundingBoxAscent, fontBoundingBoxDescent
  2337. */
  2338. NAN_METHOD(Context2d::MeasureText) {
  2339. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2340. cairo_t *ctx = context->context();
  2341. Nan::Utf8String str(Nan::To<String>(info[0]).ToLocalChecked());
  2342. Local<Object> obj = Nan::New<Object>();
  2343. PangoRectangle _ink_rect, _logical_rect;
  2344. float_rectangle ink_rect, logical_rect;
  2345. PangoFontMetrics *metrics;
  2346. PangoLayout *layout = context->layout();
  2347. pango_layout_set_text(layout, *str, -1);
  2348. pango_cairo_update_layout(ctx, layout);
  2349. // Normally you could use pango_layout_get_pixel_extents and be done, or use
  2350. // pango_extents_to_pixels, but both of those round the pixels, so we have to
  2351. // divide by PANGO_SCALE manually
  2352. pango_layout_get_extents(layout, &_ink_rect, &_logical_rect);
  2353. float inverse_pango_scale = 1. / PANGO_SCALE;
  2354. logical_rect.x = _logical_rect.x * inverse_pango_scale;
  2355. logical_rect.y = _logical_rect.y * inverse_pango_scale;
  2356. logical_rect.width = _logical_rect.width * inverse_pango_scale;
  2357. logical_rect.height = _logical_rect.height * inverse_pango_scale;
  2358. ink_rect.x = _ink_rect.x * inverse_pango_scale;
  2359. ink_rect.y = _ink_rect.y * inverse_pango_scale;
  2360. ink_rect.width = _ink_rect.width * inverse_pango_scale;
  2361. ink_rect.height = _ink_rect.height * inverse_pango_scale;
  2362. metrics = PANGO_LAYOUT_GET_METRICS(layout);
  2363. double x_offset;
  2364. switch (context->state->textAlignment) {
  2365. case TEXT_ALIGNMENT_CENTER:
  2366. x_offset = logical_rect.width / 2.;
  2367. break;
  2368. case TEXT_ALIGNMENT_END:
  2369. case TEXT_ALIGNMENT_RIGHT:
  2370. x_offset = logical_rect.width;
  2371. break;
  2372. case TEXT_ALIGNMENT_START:
  2373. case TEXT_ALIGNMENT_LEFT:
  2374. default:
  2375. x_offset = 0.0;
  2376. }
  2377. cairo_matrix_t matrix;
  2378. cairo_get_matrix(ctx, &matrix);
  2379. double y_offset = getBaselineAdjustment(layout, context->state->textBaseline);
  2380. Nan::Set(obj,
  2381. Nan::New<String>("width").ToLocalChecked(),
  2382. Nan::New<Number>(logical_rect.width)).Check();
  2383. Nan::Set(obj,
  2384. Nan::New<String>("actualBoundingBoxLeft").ToLocalChecked(),
  2385. Nan::New<Number>(PANGO_LBEARING(ink_rect) + x_offset)).Check();
  2386. Nan::Set(obj,
  2387. Nan::New<String>("actualBoundingBoxRight").ToLocalChecked(),
  2388. Nan::New<Number>(PANGO_RBEARING(ink_rect) - x_offset)).Check();
  2389. Nan::Set(obj,
  2390. Nan::New<String>("actualBoundingBoxAscent").ToLocalChecked(),
  2391. Nan::New<Number>(y_offset + PANGO_ASCENT(ink_rect))).Check();
  2392. Nan::Set(obj,
  2393. Nan::New<String>("actualBoundingBoxDescent").ToLocalChecked(),
  2394. Nan::New<Number>(PANGO_DESCENT(ink_rect) - y_offset)).Check();
  2395. Nan::Set(obj,
  2396. Nan::New<String>("emHeightAscent").ToLocalChecked(),
  2397. Nan::New<Number>(-(PANGO_ASCENT(logical_rect) - y_offset))).Check();
  2398. Nan::Set(obj,
  2399. Nan::New<String>("emHeightDescent").ToLocalChecked(),
  2400. Nan::New<Number>(PANGO_DESCENT(logical_rect) - y_offset)).Check();
  2401. Nan::Set(obj,
  2402. Nan::New<String>("alphabeticBaseline").ToLocalChecked(),
  2403. Nan::New<Number>(-(pango_font_metrics_get_ascent(metrics) * inverse_pango_scale - y_offset))).Check();
  2404. pango_font_metrics_unref(metrics);
  2405. info.GetReturnValue().Set(obj);
  2406. }
  2407. /*
  2408. * Set line dash
  2409. * ref: http://www.w3.org/TR/2dcontext/#dom-context-2d-setlinedash
  2410. */
  2411. NAN_METHOD(Context2d::SetLineDash) {
  2412. if (!info[0]->IsArray()) return;
  2413. Local<Array> dash = Local<Array>::Cast(info[0]);
  2414. uint32_t dashes = dash->Length() & 1 ? dash->Length() * 2 : dash->Length();
  2415. uint32_t zero_dashes = 0;
  2416. std::vector<double> a(dashes);
  2417. for (uint32_t i=0; i<dashes; i++) {
  2418. Local<Value> d = Nan::Get(dash, i % dash->Length()).ToLocalChecked();
  2419. if (!d->IsNumber()) return;
  2420. a[i] = Nan::To<double>(d).FromMaybe(0);
  2421. if (a[i] == 0) zero_dashes++;
  2422. if (a[i] < 0 || !std::isfinite(a[i])) return;
  2423. }
  2424. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2425. cairo_t *ctx = context->context();
  2426. double offset;
  2427. cairo_get_dash(ctx, NULL, &offset);
  2428. if (zero_dashes == dashes) {
  2429. std::vector<double> b(0);
  2430. cairo_set_dash(ctx, b.data(), 0, offset);
  2431. } else {
  2432. cairo_set_dash(ctx, a.data(), dashes, offset);
  2433. }
  2434. }
  2435. /*
  2436. * Get line dash
  2437. * ref: http://www.w3.org/TR/2dcontext/#dom-context-2d-setlinedash
  2438. */
  2439. NAN_METHOD(Context2d::GetLineDash) {
  2440. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2441. cairo_t *ctx = context->context();
  2442. int dashes = cairo_get_dash_count(ctx);
  2443. std::vector<double> a(dashes);
  2444. cairo_get_dash(ctx, a.data(), NULL);
  2445. Local<Array> dash = Nan::New<Array>(dashes);
  2446. for (int i=0; i<dashes; i++) {
  2447. Nan::Set(dash, Nan::New<Number>(i), Nan::New<Number>(a[i])).Check();
  2448. }
  2449. info.GetReturnValue().Set(dash);
  2450. }
  2451. /*
  2452. * Set line dash offset
  2453. * ref: http://www.w3.org/TR/2dcontext/#dom-context-2d-setlinedash
  2454. */
  2455. NAN_SETTER(Context2d::SetLineDashOffset) {
  2456. CHECK_RECEIVER(Context2d.SetLineDashOffset);
  2457. double offset = Nan::To<double>(value).FromMaybe(0);
  2458. if (!std::isfinite(offset)) return;
  2459. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2460. cairo_t *ctx = context->context();
  2461. int dashes = cairo_get_dash_count(ctx);
  2462. std::vector<double> a(dashes);
  2463. cairo_get_dash(ctx, a.data(), NULL);
  2464. cairo_set_dash(ctx, a.data(), dashes, offset);
  2465. }
  2466. /*
  2467. * Get line dash offset
  2468. * ref: http://www.w3.org/TR/2dcontext/#dom-context-2d-setlinedash
  2469. */
  2470. NAN_GETTER(Context2d::GetLineDashOffset) {
  2471. CHECK_RECEIVER(Context2d.GetLineDashOffset);
  2472. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2473. cairo_t *ctx = context->context();
  2474. double offset;
  2475. cairo_get_dash(ctx, NULL, &offset);
  2476. info.GetReturnValue().Set(Nan::New<Number>(offset));
  2477. }
  2478. /*
  2479. * Fill the rectangle defined by x, y, width and height.
  2480. */
  2481. NAN_METHOD(Context2d::FillRect) {
  2482. RECT_ARGS;
  2483. if (0 == width || 0 == height) return;
  2484. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2485. cairo_t *ctx = context->context();
  2486. context->savePath();
  2487. cairo_rectangle(ctx, x, y, width, height);
  2488. context->fill();
  2489. context->restorePath();
  2490. }
  2491. /*
  2492. * Stroke the rectangle defined by x, y, width and height.
  2493. */
  2494. NAN_METHOD(Context2d::StrokeRect) {
  2495. RECT_ARGS;
  2496. if (0 == width && 0 == height) return;
  2497. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2498. cairo_t *ctx = context->context();
  2499. context->savePath();
  2500. cairo_rectangle(ctx, x, y, width, height);
  2501. context->stroke();
  2502. context->restorePath();
  2503. }
  2504. /*
  2505. * Clears all pixels defined by x, y, width and height.
  2506. */
  2507. NAN_METHOD(Context2d::ClearRect) {
  2508. RECT_ARGS;
  2509. if (0 == width || 0 == height) return;
  2510. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2511. cairo_t *ctx = context->context();
  2512. cairo_save(ctx);
  2513. context->savePath();
  2514. cairo_rectangle(ctx, x, y, width, height);
  2515. cairo_set_operator(ctx, CAIRO_OPERATOR_CLEAR);
  2516. cairo_fill(ctx);
  2517. context->restorePath();
  2518. cairo_restore(ctx);
  2519. }
  2520. /*
  2521. * Adds a rectangle subpath.
  2522. */
  2523. NAN_METHOD(Context2d::Rect) {
  2524. RECT_ARGS;
  2525. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2526. cairo_t *ctx = context->context();
  2527. if (width == 0) {
  2528. cairo_move_to(ctx, x, y);
  2529. cairo_line_to(ctx, x, y + height);
  2530. } else if (height == 0) {
  2531. cairo_move_to(ctx, x, y);
  2532. cairo_line_to(ctx, x + width, y);
  2533. } else {
  2534. cairo_rectangle(ctx, x, y, width, height);
  2535. }
  2536. }
  2537. // Draws an arc with two potentially different radii.
  2538. inline static
  2539. void elli_arc(cairo_t* ctx, double xc, double yc, double rx, double ry, double a1, double a2, bool clockwise=true) {
  2540. if (rx == 0. || ry == 0.) {
  2541. cairo_line_to(ctx, xc + rx, yc + ry);
  2542. } else {
  2543. cairo_save(ctx);
  2544. cairo_translate(ctx, xc, yc);
  2545. cairo_scale(ctx, rx, ry);
  2546. if (clockwise)
  2547. cairo_arc(ctx, 0., 0., 1., a1, a2);
  2548. else
  2549. cairo_arc_negative(ctx, 0., 0., 1., a2, a1);
  2550. cairo_restore(ctx);
  2551. }
  2552. }
  2553. inline static
  2554. bool getRadius(Point<double>& p, const Local<Value>& v) {
  2555. if (v->IsObject()) { // 5.1 DOMPointInit
  2556. auto rx = Nan::Get(v.As<Object>(), Nan::New("x").ToLocalChecked()).ToLocalChecked();
  2557. auto ry = Nan::Get(v.As<Object>(), Nan::New("y").ToLocalChecked()).ToLocalChecked();
  2558. if (rx->IsNumber() && ry->IsNumber()) {
  2559. auto rxv = Nan::To<double>(rx).FromJust();
  2560. auto ryv = Nan::To<double>(ry).FromJust();
  2561. if (!std::isfinite(rxv) || !std::isfinite(ryv))
  2562. return true;
  2563. if (rxv < 0 || ryv < 0) {
  2564. Nan::ThrowRangeError("radii must be positive.");
  2565. return true;
  2566. }
  2567. p.x = rxv;
  2568. p.y = ryv;
  2569. return false;
  2570. }
  2571. } else if (v->IsNumber()) { // 5.2 unrestricted double
  2572. auto rv = Nan::To<double>(v).FromJust();
  2573. if (!std::isfinite(rv))
  2574. return true;
  2575. if (rv < 0) {
  2576. Nan::ThrowRangeError("radii must be positive.");
  2577. return true;
  2578. }
  2579. p.x = p.y = rv;
  2580. return false;
  2581. }
  2582. return true;
  2583. }
  2584. /**
  2585. * https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-roundrect
  2586. * x, y, w, h, [radius|[radii]]
  2587. */
  2588. NAN_METHOD(Context2d::RoundRect) {
  2589. RECT_ARGS;
  2590. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2591. cairo_t *ctx = context->context();
  2592. // 4. Let normalizedRadii be an empty list
  2593. Point<double> normalizedRadii[4];
  2594. size_t nRadii = 4;
  2595. if (info[4]->IsUndefined()) {
  2596. for (size_t i = 0; i < 4; i++)
  2597. normalizedRadii[i].x = normalizedRadii[i].y = 0.;
  2598. } else if (info[4]->IsArray()) {
  2599. auto radiiList = info[4].As<v8::Array>();
  2600. nRadii = radiiList->Length();
  2601. if (!(nRadii >= 1 && nRadii <= 4)) {
  2602. Nan::ThrowRangeError("radii must be a list of one, two, three or four radii.");
  2603. return;
  2604. }
  2605. // 5. For each radius of radii
  2606. for (size_t i = 0; i < nRadii; i++) {
  2607. auto r = Nan::Get(radiiList, i).ToLocalChecked();
  2608. if (getRadius(normalizedRadii[i], r))
  2609. return;
  2610. }
  2611. } else {
  2612. // 2. If radii is a double, then set radii to <<radii>>
  2613. if (getRadius(normalizedRadii[0], info[4]))
  2614. return;
  2615. for (size_t i = 1; i < 4; i++) {
  2616. normalizedRadii[i].x = normalizedRadii[0].x;
  2617. normalizedRadii[i].y = normalizedRadii[0].y;
  2618. }
  2619. }
  2620. Point<double> upperLeft, upperRight, lowerRight, lowerLeft;
  2621. if (nRadii == 4) {
  2622. upperLeft = normalizedRadii[0];
  2623. upperRight = normalizedRadii[1];
  2624. lowerRight = normalizedRadii[2];
  2625. lowerLeft = normalizedRadii[3];
  2626. } else if (nRadii == 3) {
  2627. upperLeft = normalizedRadii[0];
  2628. upperRight = normalizedRadii[1];
  2629. lowerLeft = normalizedRadii[1];
  2630. lowerRight = normalizedRadii[2];
  2631. } else if (nRadii == 2) {
  2632. upperLeft = normalizedRadii[0];
  2633. lowerRight = normalizedRadii[0];
  2634. upperRight = normalizedRadii[1];
  2635. lowerLeft = normalizedRadii[1];
  2636. } else {
  2637. upperLeft = normalizedRadii[0];
  2638. upperRight = normalizedRadii[0];
  2639. lowerRight = normalizedRadii[0];
  2640. lowerLeft = normalizedRadii[0];
  2641. }
  2642. bool clockwise = true;
  2643. if (width < 0) {
  2644. clockwise = false;
  2645. x += width;
  2646. width = -width;
  2647. std::swap(upperLeft, upperRight);
  2648. std::swap(lowerLeft, lowerRight);
  2649. }
  2650. if (height < 0) {
  2651. clockwise = !clockwise;
  2652. y += height;
  2653. height = -height;
  2654. std::swap(upperLeft, lowerLeft);
  2655. std::swap(upperRight, lowerRight);
  2656. }
  2657. // 11. Corner curves must not overlap. Scale radii to prevent this.
  2658. {
  2659. auto top = upperLeft.x + upperRight.x;
  2660. auto right = upperRight.y + lowerRight.y;
  2661. auto bottom = lowerRight.x + lowerLeft.x;
  2662. auto left = upperLeft.y + lowerLeft.y;
  2663. auto scale = std::min({ width / top, height / right, width / bottom, height / left });
  2664. if (scale < 1.) {
  2665. upperLeft.x *= scale;
  2666. upperLeft.y *= scale;
  2667. upperRight.x *= scale;
  2668. upperRight.x *= scale;
  2669. lowerLeft.y *= scale;
  2670. lowerLeft.y *= scale;
  2671. lowerRight.y *= scale;
  2672. lowerRight.y *= scale;
  2673. }
  2674. }
  2675. // 12. Draw
  2676. cairo_move_to(ctx, x + upperLeft.x, y);
  2677. if (clockwise) {
  2678. cairo_line_to(ctx, x + width - upperRight.x, y);
  2679. elli_arc(ctx, x + width - upperRight.x, y + upperRight.y, upperRight.x, upperRight.y, 3. * M_PI / 2., 0.);
  2680. cairo_line_to(ctx, x + width, y + height - lowerRight.y);
  2681. elli_arc(ctx, x + width - lowerRight.x, y + height - lowerRight.y, lowerRight.x, lowerRight.y, 0, M_PI / 2.);
  2682. cairo_line_to(ctx, x + lowerLeft.x, y + height);
  2683. elli_arc(ctx, x + lowerLeft.x, y + height - lowerLeft.y, lowerLeft.x, lowerLeft.y, M_PI / 2., M_PI);
  2684. cairo_line_to(ctx, x, y + upperLeft.y);
  2685. elli_arc(ctx, x + upperLeft.x, y + upperLeft.y, upperLeft.x, upperLeft.y, M_PI, 3. * M_PI / 2.);
  2686. } else {
  2687. elli_arc(ctx, x + upperLeft.x, y + upperLeft.y, upperLeft.x, upperLeft.y, M_PI, 3. * M_PI / 2., false);
  2688. cairo_line_to(ctx, x, y + upperLeft.y);
  2689. elli_arc(ctx, x + lowerLeft.x, y + height - lowerLeft.y, lowerLeft.x, lowerLeft.y, M_PI / 2., M_PI, false);
  2690. cairo_line_to(ctx, x + lowerLeft.x, y + height);
  2691. elli_arc(ctx, x + width - lowerRight.x, y + height - lowerRight.y, lowerRight.x, lowerRight.y, 0, M_PI / 2., false);
  2692. cairo_line_to(ctx, x + width, y + height - lowerRight.y);
  2693. elli_arc(ctx, x + width - upperRight.x, y + upperRight.y, upperRight.x, upperRight.y, 3. * M_PI / 2., 0., false);
  2694. cairo_line_to(ctx, x + width - upperRight.x, y);
  2695. }
  2696. cairo_close_path(ctx);
  2697. }
  2698. // Adapted from https://chromium.googlesource.com/chromium/blink/+/refs/heads/main/Source/modules/canvas2d/CanvasPathMethods.cpp
  2699. static void canonicalizeAngle(double& startAngle, double& endAngle) {
  2700. // Make 0 <= startAngle < 2*PI
  2701. double newStartAngle = std::fmod(startAngle, twoPi);
  2702. if (newStartAngle < 0) {
  2703. newStartAngle += twoPi;
  2704. // Check for possible catastrophic cancellation in cases where
  2705. // newStartAngle was a tiny negative number (c.f. crbug.com/503422)
  2706. if (newStartAngle >= twoPi)
  2707. newStartAngle -= twoPi;
  2708. }
  2709. double delta = newStartAngle - startAngle;
  2710. startAngle = newStartAngle;
  2711. endAngle = endAngle + delta;
  2712. }
  2713. // Adapted from https://chromium.googlesource.com/chromium/blink/+/refs/heads/main/Source/modules/canvas2d/CanvasPathMethods.cpp
  2714. static double adjustEndAngle(double startAngle, double endAngle, bool counterclockwise) {
  2715. double newEndAngle = endAngle;
  2716. /* http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arc
  2717. * If the counterclockwise argument is false and endAngle-startAngle is equal to or greater than 2pi, or,
  2718. * if the counterclockwise argument is true and startAngle-endAngle is equal to or greater than 2pi,
  2719. * then the arc is the whole circumference of this ellipse, and the point at startAngle along this circle's circumference,
  2720. * measured in radians clockwise from the ellipse's semi-major axis, acts as both the start point and the end point.
  2721. */
  2722. if (!counterclockwise && endAngle - startAngle >= twoPi)
  2723. newEndAngle = startAngle + twoPi;
  2724. else if (counterclockwise && startAngle - endAngle >= twoPi)
  2725. newEndAngle = startAngle - twoPi;
  2726. /*
  2727. * Otherwise, the arc is the path along the circumference of this ellipse from the start point to the end point,
  2728. * going anti-clockwise if the counterclockwise argument is true, and clockwise otherwise.
  2729. * Since the points are on the ellipse, as opposed to being simply angles from zero,
  2730. * the arc can never cover an angle greater than 2pi radians.
  2731. */
  2732. /* NOTE: When startAngle = 0, endAngle = 2Pi and counterclockwise = true, the spec does not indicate clearly.
  2733. * We draw the entire circle, because some web sites use arc(x, y, r, 0, 2*Math.PI, true) to draw circle.
  2734. * We preserve backward-compatibility.
  2735. */
  2736. else if (!counterclockwise && startAngle > endAngle)
  2737. newEndAngle = startAngle + (twoPi - std::fmod(startAngle - endAngle, twoPi));
  2738. else if (counterclockwise && startAngle < endAngle)
  2739. newEndAngle = startAngle - (twoPi - std::fmod(endAngle - startAngle, twoPi));
  2740. return newEndAngle;
  2741. }
  2742. /*
  2743. * Adds an arc at x, y with the given radii and start/end angles.
  2744. */
  2745. NAN_METHOD(Context2d::Arc) {
  2746. double args[5];
  2747. if(!checkArgs(info, args, 5))
  2748. return;
  2749. auto x = args[0];
  2750. auto y = args[1];
  2751. auto radius = args[2];
  2752. auto startAngle = args[3];
  2753. auto endAngle = args[4];
  2754. if (radius < 0) {
  2755. Nan::ThrowRangeError("The radius provided is negative.");
  2756. return;
  2757. }
  2758. bool counterclockwise = Nan::To<bool>(info[5]).FromMaybe(false);
  2759. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2760. cairo_t *ctx = context->context();
  2761. canonicalizeAngle(startAngle, endAngle);
  2762. endAngle = adjustEndAngle(startAngle, endAngle, counterclockwise);
  2763. if (counterclockwise) {
  2764. cairo_arc_negative(ctx, x, y, radius, startAngle, endAngle);
  2765. } else {
  2766. cairo_arc(ctx, x, y, radius, startAngle, endAngle);
  2767. }
  2768. }
  2769. /*
  2770. * Adds an arcTo point (x0,y0) to (x1,y1) with the given radius.
  2771. *
  2772. * Implementation influenced by WebKit.
  2773. */
  2774. NAN_METHOD(Context2d::ArcTo) {
  2775. double args[5];
  2776. if(!checkArgs(info, args, 5))
  2777. return;
  2778. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2779. cairo_t *ctx = context->context();
  2780. // Current path point
  2781. double x, y;
  2782. cairo_get_current_point(ctx, &x, &y);
  2783. Point<float> p0(x, y);
  2784. // Point (x0,y0)
  2785. Point<float> p1(args[0], args[1]);
  2786. // Point (x1,y1)
  2787. Point<float> p2(args[2], args[3]);
  2788. float radius = args[4];
  2789. if ((p1.x == p0.x && p1.y == p0.y)
  2790. || (p1.x == p2.x && p1.y == p2.y)
  2791. || radius == 0.f) {
  2792. cairo_line_to(ctx, p1.x, p1.y);
  2793. return;
  2794. }
  2795. Point<float> p1p0((p0.x - p1.x),(p0.y - p1.y));
  2796. Point<float> p1p2((p2.x - p1.x),(p2.y - p1.y));
  2797. float p1p0_length = sqrtf(p1p0.x * p1p0.x + p1p0.y * p1p0.y);
  2798. float p1p2_length = sqrtf(p1p2.x * p1p2.x + p1p2.y * p1p2.y);
  2799. double cos_phi = (p1p0.x * p1p2.x + p1p0.y * p1p2.y) / (p1p0_length * p1p2_length);
  2800. // all points on a line logic
  2801. if (-1 == cos_phi) {
  2802. cairo_line_to(ctx, p1.x, p1.y);
  2803. return;
  2804. }
  2805. if (1 == cos_phi) {
  2806. // add infinite far away point
  2807. unsigned int max_length = 65535;
  2808. double factor_max = max_length / p1p0_length;
  2809. Point<float> ep((p0.x + factor_max * p1p0.x), (p0.y + factor_max * p1p0.y));
  2810. cairo_line_to(ctx, ep.x, ep.y);
  2811. return;
  2812. }
  2813. float tangent = radius / tan(acos(cos_phi) / 2);
  2814. float factor_p1p0 = tangent / p1p0_length;
  2815. Point<float> t_p1p0((p1.x + factor_p1p0 * p1p0.x), (p1.y + factor_p1p0 * p1p0.y));
  2816. Point<float> orth_p1p0(p1p0.y, -p1p0.x);
  2817. float orth_p1p0_length = sqrt(orth_p1p0.x * orth_p1p0.x + orth_p1p0.y * orth_p1p0.y);
  2818. float factor_ra = radius / orth_p1p0_length;
  2819. double cos_alpha = (orth_p1p0.x * p1p2.x + orth_p1p0.y * p1p2.y) / (orth_p1p0_length * p1p2_length);
  2820. if (cos_alpha < 0.f)
  2821. orth_p1p0 = Point<float>(-orth_p1p0.x, -orth_p1p0.y);
  2822. Point<float> p((t_p1p0.x + factor_ra * orth_p1p0.x), (t_p1p0.y + factor_ra * orth_p1p0.y));
  2823. orth_p1p0 = Point<float>(-orth_p1p0.x, -orth_p1p0.y);
  2824. float sa = acos(orth_p1p0.x / orth_p1p0_length);
  2825. if (orth_p1p0.y < 0.f)
  2826. sa = 2 * M_PI - sa;
  2827. bool anticlockwise = false;
  2828. float factor_p1p2 = tangent / p1p2_length;
  2829. Point<float> t_p1p2((p1.x + factor_p1p2 * p1p2.x), (p1.y + factor_p1p2 * p1p2.y));
  2830. Point<float> orth_p1p2((t_p1p2.x - p.x),(t_p1p2.y - p.y));
  2831. float orth_p1p2_length = sqrtf(orth_p1p2.x * orth_p1p2.x + orth_p1p2.y * orth_p1p2.y);
  2832. float ea = acos(orth_p1p2.x / orth_p1p2_length);
  2833. if (orth_p1p2.y < 0) ea = 2 * M_PI - ea;
  2834. if ((sa > ea) && ((sa - ea) < M_PI)) anticlockwise = true;
  2835. if ((sa < ea) && ((ea - sa) > M_PI)) anticlockwise = true;
  2836. cairo_line_to(ctx, t_p1p0.x, t_p1p0.y);
  2837. if (anticlockwise && M_PI * 2 != radius) {
  2838. cairo_arc_negative(ctx
  2839. , p.x
  2840. , p.y
  2841. , radius
  2842. , sa
  2843. , ea);
  2844. } else {
  2845. cairo_arc(ctx
  2846. , p.x
  2847. , p.y
  2848. , radius
  2849. , sa
  2850. , ea);
  2851. }
  2852. }
  2853. /*
  2854. * Adds an ellipse to the path which is centered at (x, y) position with the
  2855. * radii radiusX and radiusY starting at startAngle and ending at endAngle
  2856. * going in the given direction by anticlockwise (defaulting to clockwise).
  2857. */
  2858. NAN_METHOD(Context2d::Ellipse) {
  2859. double args[7];
  2860. if(!checkArgs(info, args, 7))
  2861. return;
  2862. double radiusX = args[2];
  2863. double radiusY = args[3];
  2864. if (radiusX == 0 || radiusY == 0) return;
  2865. double x = args[0];
  2866. double y = args[1];
  2867. double rotation = args[4];
  2868. double startAngle = args[5];
  2869. double endAngle = args[6];
  2870. bool anticlockwise = Nan::To<bool>(info[7]).FromMaybe(false);
  2871. Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
  2872. cairo_t *ctx = context->context();
  2873. // See https://www.cairographics.org/cookbook/ellipses/
  2874. double xRatio = radiusX / radiusY;
  2875. cairo_matrix_t save_matrix;
  2876. cairo_get_matrix(ctx, &save_matrix);
  2877. cairo_translate(ctx, x, y);
  2878. cairo_rotate(ctx, rotation);
  2879. cairo_scale(ctx, xRatio, 1.0);
  2880. cairo_translate(ctx, -x, -y);
  2881. if (anticlockwise && M_PI * 2 != args[4]) {
  2882. cairo_arc_negative(ctx,
  2883. x,
  2884. y,
  2885. radiusY,
  2886. startAngle,
  2887. endAngle);
  2888. } else {
  2889. cairo_arc(ctx,
  2890. x,
  2891. y,
  2892. radiusY,
  2893. startAngle,
  2894. endAngle);
  2895. }
  2896. cairo_set_matrix(ctx, &save_matrix);
  2897. }
  2898. #undef CHECK_RECEIVER