gdiplus: Rewrite GdipDrawString to share code with GdipMeasureString.
diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c
index afbe571..e8c4d0a 100644
--- a/dlls/gdiplus/graphics.c
+++ b/dlls/gdiplus/graphics.c
@@ -2488,208 +2488,6 @@
return ret;
}
-GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
- INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
- GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
-{
- HRGN rgn = NULL;
- HFONT gdifont;
- LOGFONTW lfw;
- TEXTMETRICW textmet;
- GpPointF pt[3], rectcpy[4];
- POINT corners[4];
- WCHAR* stringdup;
- REAL angle, ang_cos, ang_sin, rel_width, rel_height;
- INT sum = 0, height = 0, offsety = 0, fit, fitcpy, save_state, i, j, lret, nwidth,
- nheight, lineend;
- SIZE size;
- POINT drawbase;
- UINT drawflags;
- RECT drawcoord;
-
- TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
- length, font, debugstr_rectf(rect), format, brush);
-
- if(!graphics || !string || !font || !brush || !rect)
- return InvalidParameter;
-
- if((brush->bt != BrushTypeSolidColor)){
- FIXME("not implemented for given parameters\n");
- return NotImplemented;
- }
-
- if(format){
- TRACE("may be ignoring some format flags: attr %x\n", format->attr);
-
- /* Should be no need to explicitly test for StringAlignmentNear as
- * that is default behavior if no alignment is passed. */
- if(format->vertalign != StringAlignmentNear){
- RectF bounds;
- GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
-
- if(format->vertalign == StringAlignmentCenter)
- offsety = (rect->Height - bounds.Height) / 2;
- else if(format->vertalign == StringAlignmentFar)
- offsety = (rect->Height - bounds.Height);
- }
- }
-
- if(length == -1) length = lstrlenW(string);
-
- stringdup = GdipAlloc(length * sizeof(WCHAR));
- if(!stringdup) return OutOfMemory;
-
- save_state = SaveDC(graphics->hdc);
- SetBkMode(graphics->hdc, TRANSPARENT);
- SetTextColor(graphics->hdc, brush->lb.lbColor);
-
- pt[0].X = 0.0;
- pt[0].Y = 0.0;
- pt[1].X = 1.0;
- pt[1].Y = 0.0;
- pt[2].X = 0.0;
- pt[2].Y = 1.0;
- GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
- angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
- ang_cos = cos(angle);
- ang_sin = sin(angle);
- rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
- (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
- rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
- (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
-
- rectcpy[3].X = rectcpy[0].X = rect->X;
- rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
- rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
- rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
- transform_and_round_points(graphics, corners, rectcpy, 4);
-
- if (roundr(rect->Width) == 0)
- nwidth = INT_MAX;
- else
- nwidth = roundr(rel_width * rect->Width);
-
- if (roundr(rect->Height) == 0)
- nheight = INT_MAX;
- else
- nheight = roundr(rel_height * rect->Height);
-
- if (roundr(rect->Width) != 0 && roundr(rect->Height) != 0)
- {
- /* FIXME: If only the width or only the height is 0, we should probably still clip */
- rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
- SelectClipRgn(graphics->hdc, rgn);
- }
-
- /* Use gdi to find the font, then perform transformations on it (height,
- * width, angle). */
- SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
- GetTextMetricsW(graphics->hdc, &textmet);
- lfw = font->lfw;
-
- lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
- lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
-
- lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
-
- gdifont = CreateFontIndirectW(&lfw);
- DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
-
- for(i = 0, j = 0; i < length; i++){
- if(!isprintW(string[i]) && (string[i] != '\n'))
- continue;
-
- stringdup[j] = string[i];
- j++;
- }
-
- length = j;
-
- if (!format || format->align == StringAlignmentNear)
- {
- drawbase.x = corners[0].x;
- drawbase.y = corners[0].y;
- drawflags = DT_NOCLIP | DT_EXPANDTABS;
- }
- else if (format->align == StringAlignmentCenter)
- {
- drawbase.x = (corners[0].x + corners[1].x)/2;
- drawbase.y = (corners[0].y + corners[1].y)/2;
- drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_CENTER;
- }
- else /* (format->align == StringAlignmentFar) */
- {
- drawbase.x = corners[1].x;
- drawbase.y = corners[1].y;
- drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_RIGHT;
- }
-
- while(sum < length){
- drawcoord.left = drawcoord.right = drawbase.x + roundr(ang_sin * (REAL) height);
- drawcoord.top = drawcoord.bottom = drawbase.y + roundr(ang_cos * (REAL) height);
-
- GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
- nwidth, &fit, NULL, &size);
- fitcpy = fit;
-
- if(fit == 0){
- DrawTextW(graphics->hdc, stringdup + sum, 1, &drawcoord, drawflags);
- break;
- }
-
- for(lret = 0; lret < fit; lret++)
- if(*(stringdup + sum + lret) == '\n')
- break;
-
- /* Line break code (may look strange, but it imitates windows). */
- if(lret < fit)
- lineend = fit = lret; /* this is not an off-by-one error */
- else if(fit < (length - sum)){
- if(*(stringdup + sum + fit) == ' ')
- while(*(stringdup + sum + fit) == ' ')
- fit++;
- else
- while(*(stringdup + sum + fit - 1) != ' '){
- fit--;
-
- if(*(stringdup + sum + fit) == '\t')
- break;
-
- if(fit == 0){
- fit = fitcpy;
- break;
- }
- }
- lineend = fit;
- while(*(stringdup + sum + lineend - 1) == ' ' ||
- *(stringdup + sum + lineend - 1) == '\t')
- lineend--;
- }
- else
- lineend = fit;
- DrawTextW(graphics->hdc, stringdup + sum, min(length - sum, lineend),
- &drawcoord, drawflags);
-
- sum += fit + (lret < fitcpy ? 1 : 0);
- height += size.cy;
-
- if(height > nheight)
- break;
-
- /* Stop if this was a linewrap (but not if it was a linebreak). */
- if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
- break;
- }
-
- GdipFree(stringdup);
- DeleteObject(rgn);
- DeleteObject(gdifont);
-
- RestoreDC(graphics->hdc, save_state);
-
- return Ok;
-}
-
GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
{
@@ -3767,6 +3565,151 @@
return Ok;
}
+struct draw_string_args {
+ POINT drawbase;
+ UINT drawflags;
+ REAL ang_cos, ang_sin;
+};
+
+static GpStatus draw_string_callback(GpGraphics *graphics,
+ GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
+ GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
+ INT lineno, const RectF *bounds, void *user_data)
+{
+ struct draw_string_args *args = user_data;
+ RECT drawcoord;
+
+ drawcoord.left = drawcoord.right = args->drawbase.x + roundr(args->ang_sin * bounds->Y);
+ drawcoord.top = drawcoord.bottom = args->drawbase.y + roundr(args->ang_cos * bounds->Y);
+
+ DrawTextW(graphics->hdc, string + index, length, &drawcoord, args->drawflags);
+
+ return Ok;
+}
+
+GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
+ INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
+ GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
+{
+ HRGN rgn = NULL;
+ HFONT gdifont;
+ LOGFONTW lfw;
+ TEXTMETRICW textmet;
+ GpPointF pt[3], rectcpy[4];
+ POINT corners[4];
+ REAL angle, rel_width, rel_height;
+ INT offsety = 0, save_state;
+ struct draw_string_args args;
+ RectF scaled_rect;
+
+ TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
+ length, font, debugstr_rectf(rect), format, brush);
+
+ if(!graphics || !string || !font || !brush || !rect)
+ return InvalidParameter;
+
+ if((brush->bt != BrushTypeSolidColor)){
+ FIXME("not implemented for given parameters\n");
+ return NotImplemented;
+ }
+
+ if(format){
+ TRACE("may be ignoring some format flags: attr %x\n", format->attr);
+
+ /* Should be no need to explicitly test for StringAlignmentNear as
+ * that is default behavior if no alignment is passed. */
+ if(format->vertalign != StringAlignmentNear){
+ RectF bounds;
+ GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
+
+ if(format->vertalign == StringAlignmentCenter)
+ offsety = (rect->Height - bounds.Height) / 2;
+ else if(format->vertalign == StringAlignmentFar)
+ offsety = (rect->Height - bounds.Height);
+ }
+ }
+
+ save_state = SaveDC(graphics->hdc);
+ SetBkMode(graphics->hdc, TRANSPARENT);
+ SetTextColor(graphics->hdc, brush->lb.lbColor);
+
+ pt[0].X = 0.0;
+ pt[0].Y = 0.0;
+ pt[1].X = 1.0;
+ pt[1].Y = 0.0;
+ pt[2].X = 0.0;
+ pt[2].Y = 1.0;
+ GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+ angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
+ args.ang_cos = cos(angle);
+ args.ang_sin = sin(angle);
+ rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
+ (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
+ rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
+ (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
+
+ rectcpy[3].X = rectcpy[0].X = rect->X;
+ rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
+ rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
+ rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
+ transform_and_round_points(graphics, corners, rectcpy, 4);
+
+ scaled_rect.X = 0.0;
+ scaled_rect.Y = 0.0;
+ scaled_rect.Width = rel_width * rect->Width;
+ scaled_rect.Height = rel_height * rect->Height;
+
+ if (roundr(scaled_rect.Width) != 0 && roundr(scaled_rect.Height) != 0)
+ {
+ /* FIXME: If only the width or only the height is 0, we should probably still clip */
+ rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
+ SelectClipRgn(graphics->hdc, rgn);
+ }
+
+ /* Use gdi to find the font, then perform transformations on it (height,
+ * width, angle). */
+ SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
+ GetTextMetricsW(graphics->hdc, &textmet);
+ lfw = font->lfw;
+
+ lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
+ lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
+
+ lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
+
+ gdifont = CreateFontIndirectW(&lfw);
+ DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
+
+ if (!format || format->align == StringAlignmentNear)
+ {
+ args.drawbase.x = corners[0].x;
+ args.drawbase.y = corners[0].y;
+ args.drawflags = DT_NOCLIP | DT_EXPANDTABS;
+ }
+ else if (format->align == StringAlignmentCenter)
+ {
+ args.drawbase.x = (corners[0].x + corners[1].x)/2;
+ args.drawbase.y = (corners[0].y + corners[1].y)/2;
+ args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_CENTER;
+ }
+ else /* (format->align == StringAlignmentFar) */
+ {
+ args.drawbase.x = corners[1].x;
+ args.drawbase.y = corners[1].y;
+ args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_RIGHT;
+ }
+
+ gdip_format_string(graphics, string, length, font, &scaled_rect, format,
+ draw_string_callback, &args);
+
+ DeleteObject(rgn);
+ DeleteObject(gdifont);
+
+ RestoreDC(graphics->hdc, save_state);
+
+ return Ok;
+}
+
GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
{
TRACE("(%p)\n", graphics);