@@ -187,7 +187,7 @@ Context2d::Context2d(Canvas *canvas) {
187187 _context = canvas->createCairoContext ();
188188 _layout = pango_cairo_create_layout (_context);
189189 state = states[stateno = 0 ] = (canvas_state_t *) malloc (sizeof (canvas_state_t ));
190-
190+
191191 resetState (true );
192192}
193193
@@ -1170,6 +1170,20 @@ NAN_METHOD(Context2d::CreateImageData){
11701170 info.GetReturnValue ().Set (instance);
11711171}
11721172
1173+ /*
1174+ * Take a transform matrix and return its components
1175+ * 0: angle, 1: scaleX, 2: scaleY, 3: skewX, 4: translateX, 5: translateY
1176+ */
1177+ void decompose_matrix (cairo_matrix_t matrix, double *destination) {
1178+ double denom = pow (matrix.xx , 2 ) + pow (matrix.yx , 2 );
1179+ destination[0 ] = atan2 (matrix.yx , matrix.xx );
1180+ destination[1 ] = sqrt (denom);
1181+ destination[2 ] = (matrix.xx * matrix.yy - matrix.xy * matrix.yx ) / destination[1 ];
1182+ destination[3 ] = atan2 (matrix.xx * matrix.xy + matrix.yx * matrix.yy , denom);
1183+ destination[4 ] = matrix.x0 ;
1184+ destination[5 ] = matrix.y0 ;
1185+ }
1186+
11731187/*
11741188 * Draw image src image to the destination (context).
11751189 *
@@ -1191,7 +1205,7 @@ NAN_METHOD(Context2d::DrawImage) {
11911205 if (!checkArgs (info, args, infoLen - 1 , 1 ))
11921206 return ;
11931207
1194- float sx = 0
1208+ double sx = 0
11951209 , sy = 0
11961210 , sw = 0
11971211 , sh = 0
@@ -1266,41 +1280,73 @@ NAN_METHOD(Context2d::DrawImage) {
12661280 // Start draw
12671281 cairo_save (ctx);
12681282
1269- // Scale src
1270- float fx = (float ) dw / sw;
1271- float fy = (float ) dh / sh;
1283+ cairo_matrix_t matrix;
1284+ double transforms[6 ];
1285+ cairo_get_matrix (context->context (), &matrix);
1286+ decompose_matrix (matrix, transforms);
1287+ // extract the scale value from the current transform so that we know how many pixels we
1288+ // need for our extra canvas in the drawImage operation.
1289+ double current_scale_x = abs (transforms[1 ]);
1290+ double current_scale_y = abs (transforms[2 ]);
1291+ double extra_dx = 0 ;
1292+ double extra_dy = 0 ;
1293+ double fx = dw / sw * current_scale_x; // transforms[1] is scale on X
1294+ double fy = dh / sh * current_scale_y; // transforms[2] is scale on X
12721295 bool needScale = dw != sw || dh != sh;
12731296 bool needCut = sw != source_w || sh != source_h || sx < 0 || sy < 0 ;
1274- bool needCairoClip = sx < 0 || sy < 0 || sw > source_w || sh > source_h;
1275-
12761297 bool sameCanvas = surface == context->canvas ()->surface ();
1277- bool needsExtraSurface = sameCanvas || needCut || needScale || needCairoClip ;
1298+ bool needsExtraSurface = sameCanvas || needCut || needScale;
12781299 cairo_surface_t *surfTemp = NULL ;
12791300 cairo_t *ctxTemp = NULL ;
12801301
12811302 if (needsExtraSurface) {
1282- surfTemp = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, dw, dh);
1303+ // we want to create the extra surface as small as possible.
1304+ // fx and fy are the total scaling we need to apply to sw, sh.
1305+ // from sw and sh we want to remove the part that is outside the source_w and soruce_h
1306+ double real_w = sw;
1307+ double real_h = sh;
1308+ double translate_x = 0 ;
1309+ double translate_y = 0 ;
1310+ // if sx or sy are negative, a part of the area represented by sw and sh is empty
1311+ // because there are empty pixels, so we cut it out.
1312+ // On the other hand if sx or sy are positive, but sw and sh extend outside the real
1313+ // source pixels, we cut the area in that case too.
1314+ if (sx < 0 ) {
1315+ extra_dx = -sx * fx;
1316+ real_w = sw + sx;
1317+ } else if (sx + sw > source_w) {
1318+ real_w = sw - (sx + sw - source_w);
1319+ }
1320+ if (sy < 0 ) {
1321+ extra_dy = -sy * fy;
1322+ real_h = sh + sy;
1323+ } else if (sy + sh > source_h) {
1324+ real_h = sh - (sy + sh - source_h);
1325+ }
1326+ // if after cutting we are still bigger than source pixels, we restrict again
1327+ if (real_w > source_w) {
1328+ real_w = source_w;
1329+ }
1330+ if (real_h > source_h) {
1331+ real_h = source_h;
1332+ }
1333+ // TODO: find a way to limit the surfTemp to real_w and real_h if fx and fy are bigger than 1.
1334+ // there are no more pixel than the one available in the source, no need to create a bigger surface.
1335+ surfTemp = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, round (real_w * fx), round (real_h * fy));
12831336 ctxTemp = cairo_create (surfTemp);
12841337 cairo_scale (ctxTemp, fx, fy);
1285- if (needCairoClip) {
1286- float clip_w = (std::min)(sw, source_w);
1287- float clip_h = (std::min)(sh, source_h);
1288- if (sx > 0 ) {
1289- clip_w -= sx;
1290- }
1291- if (sy > 0 ) {
1292- clip_h -= sy;
1293- }
1294- cairo_rectangle (ctxTemp, -sx , -sy , clip_w, clip_h);
1295- cairo_clip (ctxTemp);
1338+ if (sx > 0 ) {
1339+ translate_x = sx;
12961340 }
1297- cairo_set_source_surface (ctxTemp, surface, -sx, -sy);
1341+ if (sy > 0 ) {
1342+ translate_y = sy;
1343+ }
1344+ cairo_set_source_surface (ctxTemp, surface, -translate_x, -translate_y);
12981345 cairo_pattern_set_filter (cairo_get_source (ctxTemp), context->state ->imageSmoothingEnabled ? context->state ->patternQuality : CAIRO_FILTER_NEAREST);
12991346 cairo_pattern_set_extend (cairo_get_source (ctxTemp), CAIRO_EXTEND_REFLECT);
13001347 cairo_paint_with_alpha (ctxTemp, 1 );
13011348 surface = surfTemp;
13021349 }
1303-
13041350 // apply shadow if there is one
13051351 if (context->hasShadow ()) {
13061352 if (context->state ->shadowBlur ) {
@@ -1334,8 +1380,17 @@ NAN_METHOD(Context2d::DrawImage) {
13341380 }
13351381 }
13361382
1383+ double scaled_dx = dx;
1384+ double scaled_dy = dy;
1385+
1386+ if (needsExtraSurface && (current_scale_x != 1 || current_scale_y != 1 )) {
1387+ // in this case our surface contains already current_scale_x, we need to scale back
1388+ cairo_scale (ctx, 1 / current_scale_x, 1 / current_scale_y);
1389+ scaled_dx *= current_scale_x;
1390+ scaled_dy *= current_scale_y;
1391+ }
13371392 // Paint
1338- cairo_set_source_surface (ctx, surface, dx, dy );
1393+ cairo_set_source_surface (ctx, surface, scaled_dx + extra_dx, scaled_dy + extra_dy );
13391394 cairo_pattern_set_filter (cairo_get_source (ctx), context->state ->imageSmoothingEnabled ? context->state ->patternQuality : CAIRO_FILTER_NEAREST);
13401395 cairo_pattern_set_extend (cairo_get_source (ctx), CAIRO_EXTEND_NONE);
13411396 cairo_paint_with_alpha (ctx, context->state ->globalAlpha );
@@ -1767,7 +1822,7 @@ NAN_SETTER(Context2d::SetFillStyle) {
17671822 if (Nan::New (Gradient::constructor)->HasInstance (value) ||
17681823 Nan::New (Pattern::constructor)->HasInstance (value)) {
17691824 context->_fillStyle .Reset (value);
1770-
1825+
17711826 Local<Object> obj = Nan::To<Object>(value).ToLocalChecked ();
17721827 if (Nan::New (Gradient::constructor)->HasInstance (obj)){
17731828 Gradient *grad = Nan::ObjectWrap::Unwrap<Gradient>(obj);
@@ -1813,7 +1868,7 @@ NAN_SETTER(Context2d::SetStrokeStyle) {
18131868 if (Nan::New (Gradient::constructor)->HasInstance (value) ||
18141869 Nan::New (Pattern::constructor)->HasInstance (value)) {
18151870 context->_strokeStyle .Reset (value);
1816-
1871+
18171872 Local<Object> obj = Nan::To<Object>(value).ToLocalChecked ();
18181873 if (Nan::New (Gradient::constructor)->HasInstance (obj)){
18191874 Gradient *grad = Nan::ObjectWrap::Unwrap<Gradient>(obj);
@@ -2438,7 +2493,7 @@ NAN_GETTER(Context2d::GetFont) {
24382493 * - size
24392494 * - unit
24402495 * - family
2441- */
2496+ */
24422497
24432498NAN_SETTER (Context2d::SetFont) {
24442499 if (!value->IsString ()) return ;
0 commit comments