summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Plomer <mail@mark-plomer.de>2016-04-03 11:52:59 +0200
committerMark Plomer <mail@mark-plomer.de>2016-04-03 12:11:26 +0200
commitf5f94d9c089cb670e9c119fff4102af1ffc7fbe5 (patch)
treebee5ea5bc7992aba6c3657f3a4fef67165c97baa
parent9f25f736ed50a2dc94aeff4f289c1a5e26fc6a40 (diff)
downloadphp-git-f5f94d9c089cb670e9c119fff4102af1ffc7fbe5.tar.gz
fix left-shifted/misaligned bounding-box + wrong kerning in imagettfbbox/imageftbbox
- load glyph with FT_LOAD_IGNORE_TRANSFORM for bbox as final bbox is rotated at once later (fixes "double-rotation" per glyph for calculating bbox) - reload the rotated glyph for painting after that (only if angle != 0) - rotate the original bbox at 0,0 and do not throw away xMin/yMin (drawing-rotation is also based at "origin" point - including the bearingX, see http://www.freetype.org/freetype2/docs/glyphs/glyphs-3.html#section-3) - this fixes the "left-shift"-problem also when angle = 0 - removed "xb/yb" and use "x/y" directly for offsetting brect (no need for adding "x1/y1" and substracting "yd" later) - removed therefore unused "yd" helper var which seems tried to fix parts of the original problems - initialize x/y with 0 instead of -1 in php_imagettftext_common() to make image*text() and image*bbox() results identical (there was a -1px shift in image*bbox() before) - fixed gdroundupdown() for negative numbers (-256 / 64 gives -5 instead of -4 before) - rotate kerning-delta by given angle (fixes completely wrong kerning and therefore wrong bounding box if angle != 0) - changed 3 tests and added a new one to test for the new (better) coordinates
-rw-r--r--ext/gd/gd.c2
-rw-r--r--ext/gd/libgd/gdft.c127
-rw-r--r--ext/gd/tests/bug43073_1.phpt34
-rw-r--r--ext/gd/tests/bug48732.phpt2
-rw-r--r--ext/gd/tests/bug48801_1.phpt8
-rw-r--r--ext/gd/tests/bug53504.phpt101
6 files changed, 191 insertions, 83 deletions
diff --git a/ext/gd/gd.c b/ext/gd/gd.c
index b7203e7b8c..d9d2b92e7b 100644
--- a/ext/gd/gd.c
+++ b/ext/gd/gd.c
@@ -3823,7 +3823,7 @@ static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int mode, int
{
zval *IM, *EXT = NULL;
gdImagePtr im=NULL;
- long col = -1, x = -1, y = -1;
+ long col = -1, x = 0, y = 0;
int str_len, fontname_len, i, brect[8];
double ptsize, angle;
char *str = NULL, *fontname = NULL;
diff --git a/ext/gd/libgd/gdft.c b/ext/gd/libgd/gdft.c
index 763efcf68c..b74c4938ea 100644
--- a/ext/gd/libgd/gdft.c
+++ b/ext/gd/libgd/gdft.c
@@ -748,9 +748,9 @@ static char * gdft_draw_bitmap (gdCache_head_t *tc_cache, gdImage * im, int fg,
}
static int
-gdroundupdown (FT_F26Dot6 v1, int updown)
+gdroundupdown (FT_F26Dot6 v1, int roundup)
{
- return (!updown) ? (v1 < 0 ? ((v1 - 63) >> 6) : v1 >> 6) : (v1 > 0 ? ((v1 + 63) >> 6) : v1 >> 6);
+ return (!roundup) ? v1 >> 6 : (v1 + 63) >> 6;
}
void gdFontCacheShutdown()
@@ -820,8 +820,6 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
double cos_a = cos (angle);
int len, i = 0, ch;
int x1 = 0, y1 = 0;
- int xb = x, yb = y;
- int yd = 0;
font_t *font;
fontkey_t fontkey;
char *next;
@@ -982,9 +980,6 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
penf.y = (penf.y - 32) & -64; /* round to next pixel row */
x1 = (int)(- penf.y * sin_a + 32) / 64;
y1 = (int)(- penf.y * cos_a + 32) / 64;
- xb = x + x1;
- yb = y + y1;
- yd = 0;
pen.x = pen.y = 0;
previous = 0; /* clear kerning flag */
continue;
@@ -1074,31 +1069,32 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
/* retrieve kerning distance and move pen position */
if (use_kerning && previous && glyph_index) {
FT_Get_Kerning(face, previous, glyph_index, ft_kerning_default, &delta);
- pen.x += delta.x;
+ pen.x += (int)(delta.x * cos_a);
+ pen.y -= (int)(delta.x * sin_a);
penf.x += delta.x;
}
- /* load glyph image into the slot (erase previous one) */
- if (FT_Load_Glyph(face, glyph_index, render_mode)) {
- if (tmpstr) {
- gdFree(tmpstr);
+ if (brect) { /* only if need brect */
+ /* load glyph image into the slot (erase previous one) */
+ if (FT_Load_Glyph(face, glyph_index, render_mode | FT_LOAD_IGNORE_TRANSFORM)) {
+ if (tmpstr) {
+ gdFree(tmpstr);
+ }
+ gdCacheDelete(tc_cache);
+ gdMutexUnlock(gdFontCacheMutex);
+ return "Problem loading glyph";
}
- gdCacheDelete(tc_cache);
- gdMutexUnlock(gdFontCacheMutex);
- return "Problem loading glyph";
- }
- /* transform glyph image */
- if (FT_Get_Glyph(slot, &image)) {
- if (tmpstr) {
- gdFree(tmpstr);
+ /* transform glyph image */
+ if (FT_Get_Glyph(slot, &image)) {
+ if (tmpstr) {
+ gdFree(tmpstr);
+ }
+ gdCacheDelete(tc_cache);
+ gdMutexUnlock(gdFontCacheMutex);
+ return "Problem loading glyph";
}
- gdCacheDelete(tc_cache);
- gdMutexUnlock(gdFontCacheMutex);
- return "Problem loading glyph";
- }
- if (brect) { /* only if need brect */
FT_Glyph_Get_CBox(image, ft_glyph_bbox_gridfit, &glyph_bbox);
glyph_bbox.xMin += penf.x;
glyph_bbox.yMin += penf.y;
@@ -1108,17 +1104,11 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
glyph_bbox.xMax += slot->metrics.horiAdvance;
}
if (!i) { /* if first character, init BB corner values */
- yd = slot->metrics.height - slot->metrics.horiBearingY;
bbox.xMin = glyph_bbox.xMin;
bbox.yMin = glyph_bbox.yMin;
bbox.xMax = glyph_bbox.xMax;
bbox.yMax = glyph_bbox.yMax;
} else {
- FT_Pos desc;
-
- if ( (desc = (slot->metrics.height - slot->metrics.horiBearingY)) > yd) {
- yd = desc;
- }
if (bbox.xMin > glyph_bbox.xMin) {
bbox.xMin = glyph_bbox.xMin;
}
@@ -1135,7 +1125,35 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
i++;
}
+ /* increment (unrotated) pen position */
+ penf.x += slot->metrics.horiAdvance;
+
if (render) {
+ if (!brect || angle != 0) {
+ /* reload the rotated glyph (for bbox we needed FT_LOAD_IGNORE_TRANSFORM - bbox is rotated later) */
+ FT_Done_Glyph(image);
+
+ /* load glyph image into the slot (erase previous one) */
+ if (FT_Load_Glyph(face, glyph_index, render_mode)) {
+ if (tmpstr) {
+ gdFree(tmpstr);
+ }
+ gdCacheDelete(tc_cache);
+ gdMutexUnlock(gdFontCacheMutex);
+ return "Problem loading glyph";
+ }
+
+ /* transform glyph image */
+ if (FT_Get_Glyph(slot, &image)) {
+ if (tmpstr) {
+ gdFree(tmpstr);
+ }
+ gdCacheDelete(tc_cache);
+ gdMutexUnlock(gdFontCacheMutex);
+ return "Problem loading glyph";
+ }
+ }
+
if (image->format != ft_glyph_format_bitmap && FT_Glyph_To_Bitmap(&image, ft_render_mode_normal, 0, 1)) {
FT_Done_Glyph(image);
if (tmpstr) {
@@ -1158,8 +1176,6 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
pen.x += image->advance.x >> 10;
pen.y -= image->advance.y >> 10;
- penf.x += slot->metrics.horiAdvance;
-
FT_Done_Glyph(image);
}
@@ -1168,36 +1184,25 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
double d1 = sin (angle + 0.78539816339744830962);
double d2 = sin (angle - 0.78539816339744830962);
- /* make the center of rotation at (0, 0) */
- FT_BBox normbox;
-
- normbox.xMin = 0;
- normbox.yMin = 0;
- normbox.xMax = bbox.xMax - bbox.xMin;
- normbox.yMax = bbox.yMax - bbox.yMin;
-
- brect[0] = brect[2] = brect[4] = brect[6] = (int) (yd * sin_a);
- brect[1] = brect[3] = brect[5] = brect[7] = (int)(- yd * cos_a);
-
- /* rotate bounding rectangle */
- brect[0] += (int) (normbox.xMin * cos_a - normbox.yMin * sin_a);
- brect[1] += (int) (normbox.xMin * sin_a + normbox.yMin * cos_a);
- brect[2] += (int) (normbox.xMax * cos_a - normbox.yMin * sin_a);
- brect[3] += (int) (normbox.xMax * sin_a + normbox.yMin * cos_a);
- brect[4] += (int) (normbox.xMax * cos_a - normbox.yMax * sin_a);
- brect[5] += (int) (normbox.xMax * sin_a + normbox.yMax * cos_a);
- brect[6] += (int) (normbox.xMin * cos_a - normbox.yMax * sin_a);
- brect[7] += (int) (normbox.xMin * sin_a + normbox.yMax * cos_a);
+ /* rotate bounding rectangle (at 0, 0) */
+ brect[0] = (int) (bbox.xMin * cos_a - bbox.yMin * sin_a);
+ brect[1] = (int) (bbox.xMin * sin_a + bbox.yMin * cos_a);
+ brect[2] = (int) (bbox.xMax * cos_a - bbox.yMin * sin_a);
+ brect[3] = (int) (bbox.xMax * sin_a + bbox.yMin * cos_a);
+ brect[4] = (int) (bbox.xMax * cos_a - bbox.yMax * sin_a);
+ brect[5] = (int) (bbox.xMax * sin_a + bbox.yMax * cos_a);
+ brect[6] = (int) (bbox.xMin * cos_a - bbox.yMax * sin_a);
+ brect[7] = (int) (bbox.xMin * sin_a + bbox.yMax * cos_a);
/* scale, round and offset brect */
- brect[0] = xb + gdroundupdown(brect[0], d2 > 0);
- brect[1] = yb - gdroundupdown(brect[1], d1 < 0);
- brect[2] = xb + gdroundupdown(brect[2], d1 > 0);
- brect[3] = yb - gdroundupdown(brect[3], d2 > 0);
- brect[4] = xb + gdroundupdown(brect[4], d2 < 0);
- brect[5] = yb - gdroundupdown(brect[5], d1 > 0);
- brect[6] = xb + gdroundupdown(brect[6], d1 < 0);
- brect[7] = yb - gdroundupdown(brect[7], d2 < 0);
+ brect[0] = x + gdroundupdown(brect[0], d2 > 0);
+ brect[1] = y - gdroundupdown(brect[1], d1 < 0);
+ brect[2] = x + gdroundupdown(brect[2], d1 > 0);
+ brect[3] = y - gdroundupdown(brect[3], d2 > 0);
+ brect[4] = x + gdroundupdown(brect[4], d2 < 0);
+ brect[5] = y - gdroundupdown(brect[5], d1 > 0);
+ brect[6] = x + gdroundupdown(brect[6], d1 < 0);
+ brect[7] = y - gdroundupdown(brect[7], d2 < 0);
}
if (tmpstr) {
diff --git a/ext/gd/tests/bug43073_1.phpt b/ext/gd/tests/bug43073_1.phpt
index b69067d31b..a0682bcf63 100644
--- a/ext/gd/tests/bug43073_1.phpt
+++ b/ext/gd/tests/bug43073_1.phpt
@@ -16,12 +16,14 @@ $delta_t = 360.0 / 16; # Make 16 steps around
$g = imagecreate(800, 800);
$bgnd = imagecolorallocate($g, 255, 255, 255);
$black = imagecolorallocate($g, 0, 0, 0);
+$red = imagecolorallocate($g, 255, 0, 0);
$x = 100;
$y = 0;
$cos_t = cos(deg2rad($delta_t));
$sin_t = sin(deg2rad($delta_t));
for ($angle = 0.0; $angle < 360.0; $angle += $delta_t) {
$bbox = imagettftext($g, 24, $angle, 400+$x, 400+$y, $black, $font, 'ABCDEF');
+ imagepolygon($g, $bbox, 4, $red);
$s = vsprintf("(%d, %d), (%d, %d), (%d, %d), (%d, %d)\n", $bbox);
echo $s;
$temp = $cos_t * $x + $sin_t * $y;
@@ -33,19 +35,19 @@ imagepng($g, "$cwd/bug43073.png");
--CLEAN--
<?php @unlink(dirname(__FILE__) . '/bug43073.png'); ?>
--EXPECTF--
-(500, 400), (610, 400), (610, 376), (500, 376)
-(492, 363), (591, 322), (580, 295), (480, 336)
-(470, 331), (548, 254), (527, 233), (449, 310)
-(439, 309), (483, 202), (461, 193), (416, 299)
-(400, 300), (400, 183), (380, 183), (380, 300)
-(362, 307), (316, 195), (291, 205), (337, 318)
-(330, 329), (246, 244), (224, 265), (308, 350)
-(308, 360), (202, 316), (190, 344), (296, 388)
-(300, 400), (187, 400), (187, 425), (300, 425)
-(306, 437), (195, 483), (206, 510), (318, 464)
-(328, 469), (240, 557), (260, 578), (349, 491)
-(359, 491), (312, 607), (334, 616), (382, 501)
-(400, 500), (400, 618), (419, 618), (419, 500)
-(436, 493), (483, 607), (507, 597), (461, 482)
-(468, 471), (555, 558), (577, 538), (490, 450)
-(490, 440), (600, 485), (611, 457), (502, 412)
+(501, 400), (611, 400), (611, 376), (501, 376)
+(492, 361), (595, 319), (586, 296), (483, 338)
+(470, 329), (549, 251), (531, 233), (453, 312)
+(439, 307), (481, 204), (458, 195), (416, 297)
+(400, 299), (400, 189), (376, 189), (376, 299)
+(361, 307), (319, 204), (296, 213), (338, 316)
+(329, 329), (251, 250), (233, 267), (311, 346)
+(307, 360), (204, 318), (195, 341), (297, 383)
+(299, 400), (189, 400), (189, 424), (299, 424)
+(307, 438), (204, 480), (213, 503), (316, 461)
+(329, 470), (250, 548), (267, 566), (346, 488)
+(360, 492), (318, 595), (341, 604), (383, 502)
+(400, 501), (400, 611), (424, 611), (424, 501)
+(438, 492), (480, 595), (503, 586), (461, 483)
+(470, 470), (548, 549), (566, 532), (488, 453)
+(492, 439), (595, 481), (604, 458), (502, 416)
diff --git a/ext/gd/tests/bug48732.phpt b/ext/gd/tests/bug48732.phpt
index f8cb5e2bac..38dc861a9d 100644
--- a/ext/gd/tests/bug48732.phpt
+++ b/ext/gd/tests/bug48732.phpt
@@ -19,4 +19,4 @@ echo 'Left Bottom: (' . $bbox[0] . ', ' . $bbox[1] . ')';
--CLEAN--
<?php @unlink(dirname(__FILE__) . '/bug48732.png'); ?>
--EXPECTF--
-Left Bottom: (0, 47)
+Left Bottom: (0, 46)
diff --git a/ext/gd/tests/bug48801_1.phpt b/ext/gd/tests/bug48801_1.phpt
index 0ab6d7cdd9..f0f3f93fd4 100644
--- a/ext/gd/tests/bug48801_1.phpt
+++ b/ext/gd/tests/bug48801_1.phpt
@@ -19,7 +19,7 @@ echo '(' . $bbox[4] . ', ' . $bbox[5] . ")\n";
echo '(' . $bbox[6] . ', ' . $bbox[7] . ")\n";
?>
--EXPECTF--
-(-1, 15)
-(15%d, 15)
-(15%d, -48)
-(-1, -48)
+(4, 15)
+(161, 15)
+(161, -47)
+(4, -47)
diff --git a/ext/gd/tests/bug53504.phpt b/ext/gd/tests/bug53504.phpt
new file mode 100644
index 0000000000..c84ee78fdb
--- /dev/null
+++ b/ext/gd/tests/bug53504.phpt
@@ -0,0 +1,101 @@
+--TEST--
+Bug #53504 imagettfbbox/imageftbbox gives incorrect values for bounding box
+--SKIPIF--
+<?php
+ if(!extension_loaded('gd')){ die('skip gd extension not available'); }
+ if(!function_exists('imageftbbox')) die('skip imageftbbox() not available');
+
+ include dirname(__FILE__) . '/func.inc';
+ if(version_compare(get_freetype_version(), '2.4.10') == -1) die('skip for freetype < 2.4.10');
+?>
+--FILE--
+<?php
+$cwd = dirname(__FILE__);
+$font = "$cwd/Tuffy.ttf";
+
+$g = imagecreate(800, 800);
+$bgnd = imagecolorallocate($g, 255, 255, 255);
+$black = imagecolorallocate($g, 0, 0, 0);
+$red = imagecolorallocate($g, 255, 0, 0);
+$blue = imagecolorallocate($g, 0, 0, 255);
+
+$tests = [
+ // Kerning examples (unfortunately not available in "Tuffy" test font):
+ ['fontSize' => 50, 'angle' => 0, 'x' => 20, 'y' => 70, 'text' => 'AV Teg'],
+ ['fontSize' => 50, 'angle' => 90, 'x' => 70, 'y' => 350, 'text' => 'AV Teg'],
+ ['fontSize' => 50, 'angle' => 40, 'x' => 130, 'y' => 280, 'text' => 'AV Teg'],
+
+ // Shift-Test:
+ ['fontSize' => 100, 'angle' => 0, 'x' => 350, 'y' => 110, 'text' => 'H-Shift'],
+
+ // Small/single chars:
+ ['fontSize' => 100, 'angle' => 0, 'x' => 350, 'y' => 220, 'text' => '-'],
+ ['fontSize' => 100, 'angle' => 0, 'x' => 430, 'y' => 220, 'text' => ','],
+ ['fontSize' => 100, 'angle' => 0, 'x' => 510, 'y' => 220, 'text' => '.'],
+ ['fontSize' => 100, 'angle' => 0, 'x' => 590, 'y' => 220, 'text' => '|'],
+ ['fontSize' => 100, 'angle' => 0, 'x' => 670, 'y' => 220, 'text' => 'g'],
+
+ // Multi-Line + rotation:
+ ['fontSize' => 30, 'angle' => 0, 'x' => 20, 'y' => 400, 'text' => "Multi\nLine\nTest"],
+ ['fontSize' => 30, 'angle' => 40, 'x' => 150, 'y' => 420, 'text' => "Multi\nLine\nTest"],
+ ['fontSize' => 30, 'angle' => 90, 'x' => 250, 'y' => 340, 'text' => "Multi\nLine\nTest"],
+
+ // Some edge case glyphs:
+ ['fontSize' => 50, 'angle' => 90, 'x' => 70, 'y' => 750, 'text' => "iiiiiiiiiiii"],
+ ['fontSize' => 50, 'angle' => 90, 'x' => 150, 'y' => 750, 'text' => "~~~~~~~"],
+ ['fontSize' => 50, 'angle' => 50, 'x' => 210, 'y' => 750, 'text' => "iiiiiiiiiiii"],
+ ['fontSize' => 50, 'angle' => 50, 'x' => 300, 'y' => 750, 'text' => "~~~~~~~"],
+ ['fontSize' => 50, 'angle' => 0, 'x' => 430, 'y' => 650, 'text' => "iiiiiiiiiiii"],
+ ['fontSize' => 50, 'angle' => 0, 'x' => 430, 'y' => 750, 'text' => "~~~~~~~"],
+
+ // "Big" test:
+ ['fontSize' => 200, 'angle' => 0, 'x' => 400, 'y' => 500, 'text' => "Big"],
+];
+
+foreach ($tests as $test) {
+ $bbox = imageftbbox($test['fontSize'], $test['angle'], $font, $test['text']);
+ vprintf("(%d, %d), (%d, %d), (%d, %d), (%d, %d)\n", $bbox);
+
+ $bboxDrawn = imagefttext($g, $test['fontSize'], $test['angle'],
+ $test['x'], $test['y'], $black, $font, $test['text']);
+
+ // check if both bboxes match when adding x/y offset:
+ for ($i = 0; $i < count($bbox); $i += 2) {
+ if ($bbox[$i] + $test['x'] !== $bboxDrawn[$i]) echo "imageftbbox and imagefttext differ!\n";
+ if ($bbox[$i + 1] + $test['y'] !== $bboxDrawn[$i + 1]) echo "imageftbbox and imagefttext differ!\n";
+ }
+
+ // draw bounding box:
+ imagepolygon($g, $bboxDrawn, 4, $red);
+
+ // draw baseline:
+ $width = sqrt(pow($bboxDrawn[2] - $bboxDrawn[0], 2) + pow($bboxDrawn[3] - $bboxDrawn[1], 2));
+ imageline($g, $test['x'], $test['y'],
+ $test['x'] + $width * cos(deg2rad($test['angle'])),
+ $test['y'] - $width * sin(deg2rad($test['angle'])), $blue);
+}
+
+imagepng($g, "$cwd/bug53504.png");
+?>
+--CLEAN--
+<?php @unlink(dirname(__FILE__) . '/bug53504.png'); ?>
+--EXPECTF--
+(2, 15), (208, 15), (208, -48), (2, -48)
+(15, -1), (15, -208), (-48, -208), (-48, -2)
+(11, 11), (169, -122), (129, -171), (-30, -39)
+(8, 2), (385, 2), (385, -97), (8, -97)
+(7, -37), (51, -37), (51, -46), (7, -46)
+(7, 15), (21, 15), (21, -13), (7, -13)
+(7, 1), (21, 1), (21, -13), (7, -13)
+(8, 0), (17, 0), (17, -95), (8, -95)
+(5, 29), (60, 29), (60, -72), (5, -72)
+(2, 107), (80, 107), (80, -29), (2, -29)
+(70, 81), (131, 31), (43, -74), (-18, -24)
+(107, -1), (107, -80), (-29, -80), (-29, -2)
+(0, -4), (0, -165), (-47, -165), (-47, -4)
+(-19, -2), (-18, -167), (-29, -167), (-29, -2)
+(3, -3), (107, -127), (70, -157), (-34, -33)
+(-13, -13), (93, -141), (85, -147), (-21, -20)
+(4, 0), (165, 0), (165, -47), (4, -47)
+(2, -19), (167, -19), (167, -29), (2, -29)
+(16, 59), (330, 59), (330, -190), (16, -190)