diff --git a/reactos/dll/win32/gdiplus/brush.c b/reactos/dll/win32/gdiplus/brush.c index 7f26a1ea505..2ada393f5df 100644 --- a/reactos/dll/win32/gdiplus/brush.c +++ b/reactos/dll/win32/gdiplus/brush.c @@ -105,13 +105,7 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone) return stat; } - stat = GdipCloneMatrix(src->transform, &dest->transform); - - if(stat != Ok){ - GdipDeletePath(dest->path); - GdipFree(dest); - return stat; - } + dest->transform = src->transform; /* blending */ count = src->blendcount; @@ -129,7 +123,6 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone) if(!dest->blendfac || !dest->blendpos || !dest->surroundcolors || (pcount && (!dest->pblendcolor || !dest->pblendpos))){ GdipDeletePath(dest->path); - GdipDeleteMatrix(dest->transform); GdipFree(dest->blendfac); GdipFree(dest->blendpos); GdipFree(dest->surroundcolors); @@ -211,7 +204,7 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone) if (stat == Ok) { - memcpy(new_texture->transform, texture->transform, sizeof(GpMatrix)); + new_texture->transform = texture->transform; *clone = (GpBrush*)new_texture; } else @@ -539,7 +532,6 @@ GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngleI(GDIPCONST GpRect* rect static GpStatus create_path_gradient(GpPath *path, ARGB centercolor, GpPathGradient **grad) { GpRectF bounds; - GpStatus stat; if(!path || !grad) return InvalidParameter; @@ -555,18 +547,12 @@ static GpStatus create_path_gradient(GpPath *path, ARGB centercolor, GpPathGradi return OutOfMemory; } - stat = GdipCreateMatrix(&(*grad)->transform); - if (stat != Ok) - { - GdipFree(*grad); - return stat; - } + GdipSetMatrixElements(&(*grad)->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); (*grad)->blendfac = GdipAlloc(sizeof(REAL)); (*grad)->blendpos = GdipAlloc(sizeof(REAL)); (*grad)->surroundcolors = GdipAlloc(sizeof(ARGB)); if(!(*grad)->blendfac || !(*grad)->blendpos || !(*grad)->surroundcolors){ - GdipDeleteMatrix((*grad)->transform); GdipFree((*grad)->blendfac); GdipFree((*grad)->blendpos); GdipFree((*grad)->surroundcolors); @@ -819,9 +805,7 @@ GpStatus WINGDIPAPI GdipCreateTextureIA(GpImage *image, goto exit; } - if((status = GdipCreateMatrix(&(*texture)->transform)) != Ok){ - goto exit; - } + GdipSetMatrixElements(&(*texture)->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); if (imageattr) { @@ -848,7 +832,6 @@ exit: { if (*texture) { - GdipDeleteMatrix((*texture)->transform); GdipDisposeImageAttributes((*texture)->imageattributes); GdipFree(*texture); *texture = NULL; @@ -947,7 +930,6 @@ GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush) { case BrushTypePathGradient: GdipDeletePath(((GpPathGradient*) brush)->path); - GdipDeleteMatrix(((GpPathGradient*) brush)->transform); GdipFree(((GpPathGradient*) brush)->blendfac); GdipFree(((GpPathGradient*) brush)->blendpos); GdipFree(((GpPathGradient*) brush)->surroundcolors); @@ -961,7 +943,6 @@ GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush) GdipFree(((GpLineGradient*)brush)->pblendpos); break; case BrushTypeTextureFill: - GdipDeleteMatrix(((GpTexture*)brush)->transform); GdipDisposeImage(((GpTexture*)brush)->image); GdipDisposeImageAttributes(((GpTexture*)brush)->imageattributes); GdipFree(((GpTexture*)brush)->bitmap_bits); @@ -1059,8 +1040,8 @@ GpStatus WINGDIPAPI GdipGetPathGradientCenterPointI(GpPathGradient *grad, ret = GdipGetPathGradientCenterPoint(grad,&ptf); if(ret == Ok){ - point->X = roundr(ptf.X); - point->Y = roundr(ptf.Y); + point->X = gdip_round(ptf.X); + point->Y = gdip_round(ptf.Y); } return ret; @@ -1158,10 +1139,10 @@ GpStatus WINGDIPAPI GdipGetPathGradientRectI(GpPathGradient *brush, GpRect *rect stat = GdipGetPathGradientRect(brush, &rectf); if(stat != Ok) return stat; - rect->X = roundr(rectf.X); - rect->Y = roundr(rectf.Y); - rect->Width = roundr(rectf.Width); - rect->Height = roundr(rectf.Height); + rect->X = gdip_round(rectf.X); + rect->Y = gdip_round(rectf.Y); + rect->Width = gdip_round(rectf.Width); + rect->Height = gdip_round(rectf.Height); return Ok; } @@ -1253,7 +1234,7 @@ GpStatus WINGDIPAPI GdipGetTextureTransform(GpTexture *brush, GpMatrix *matrix) if(!brush || !matrix) return InvalidParameter; - memcpy(matrix, brush->transform, sizeof(GpMatrix)); + *matrix = brush->transform; return Ok; } @@ -1284,7 +1265,7 @@ GpStatus WINGDIPAPI GdipMultiplyTextureTransform(GpTexture* brush, if(!brush || !matrix) return InvalidParameter; - return GdipMultiplyMatrix(brush->transform, matrix, order); + return GdipMultiplyMatrix(&brush->transform, matrix, order); } /****************************************************************************** @@ -1297,7 +1278,7 @@ GpStatus WINGDIPAPI GdipResetTextureTransform(GpTexture* brush) if(!brush) return InvalidParameter; - return GdipSetMatrixElements(brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + return GdipSetMatrixElements(&brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); } /****************************************************************************** @@ -1311,7 +1292,7 @@ GpStatus WINGDIPAPI GdipScaleTextureTransform(GpTexture* brush, if(!brush) return InvalidParameter; - return GdipScaleMatrix(brush->transform, sx, sy, order); + return GdipScaleMatrix(&brush->transform, sx, sy, order); } GpStatus WINGDIPAPI GdipSetLineBlend(GpLineGradient *brush, @@ -1785,7 +1766,7 @@ GpStatus WINGDIPAPI GdipSetPathGradientTransform(GpPathGradient *grad, if (!grad || !matrix) return InvalidParameter; - memcpy(grad->transform, matrix, sizeof(GpMatrix)); + grad->transform = *matrix; return Ok; } @@ -1798,7 +1779,7 @@ GpStatus WINGDIPAPI GdipGetPathGradientTransform(GpPathGradient *grad, if (!grad || !matrix) return InvalidParameter; - memcpy(matrix, grad->transform, sizeof(GpMatrix)); + *matrix = grad->transform; return Ok; } @@ -1811,7 +1792,7 @@ GpStatus WINGDIPAPI GdipMultiplyPathGradientTransform(GpPathGradient *grad, if (!grad) return InvalidParameter; - return GdipMultiplyMatrix(grad->transform, matrix, order); + return GdipMultiplyMatrix(&grad->transform, matrix, order); } GpStatus WINGDIPAPI GdipResetPathGradientTransform(GpPathGradient *grad) @@ -1821,7 +1802,7 @@ GpStatus WINGDIPAPI GdipResetPathGradientTransform(GpPathGradient *grad) if (!grad) return InvalidParameter; - return GdipSetMatrixElements(grad->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + return GdipSetMatrixElements(&grad->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); } GpStatus WINGDIPAPI GdipRotatePathGradientTransform(GpPathGradient *grad, @@ -1832,7 +1813,7 @@ GpStatus WINGDIPAPI GdipRotatePathGradientTransform(GpPathGradient *grad, if (!grad) return InvalidParameter; - return GdipRotateMatrix(grad->transform, angle, order); + return GdipRotateMatrix(&grad->transform, angle, order); } GpStatus WINGDIPAPI GdipScalePathGradientTransform(GpPathGradient *grad, @@ -1843,7 +1824,7 @@ GpStatus WINGDIPAPI GdipScalePathGradientTransform(GpPathGradient *grad, if (!grad) return InvalidParameter; - return GdipScaleMatrix(grad->transform, sx, sy, order); + return GdipScaleMatrix(&grad->transform, sx, sy, order); } GpStatus WINGDIPAPI GdipTranslatePathGradientTransform(GpPathGradient *grad, @@ -1854,7 +1835,7 @@ GpStatus WINGDIPAPI GdipTranslatePathGradientTransform(GpPathGradient *grad, if (!grad) return InvalidParameter; - return GdipTranslateMatrix(grad->transform, dx, dy, order); + return GdipTranslateMatrix(&grad->transform, dx, dy, order); } GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb) @@ -1879,7 +1860,7 @@ GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture, if(!texture || !matrix) return InvalidParameter; - memcpy(texture->transform, matrix, sizeof(GpMatrix)); + texture->transform = *matrix; return Ok; } @@ -1939,7 +1920,7 @@ GpStatus WINGDIPAPI GdipRotateTextureTransform(GpTexture* brush, REAL angle, if(!brush) return InvalidParameter; - return GdipRotateMatrix(brush->transform, angle, order); + return GdipRotateMatrix(&brush->transform, angle, order); } GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus, @@ -2125,7 +2106,7 @@ GpStatus WINGDIPAPI GdipTranslateTextureTransform(GpTexture* brush, REAL dx, REA if(!brush) return InvalidParameter; - return GdipTranslateMatrix(brush->transform, dx, dy, order); + return GdipTranslateMatrix(&brush->transform, dx, dy, order); } GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect) @@ -2153,10 +2134,10 @@ GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect) ret = GdipGetLineRect(brush, &rectF); if(ret == Ok){ - rect->X = roundr(rectF.X); - rect->Y = roundr(rectF.Y); - rect->Width = roundr(rectF.Width); - rect->Height = roundr(rectF.Height); + rect->X = gdip_round(rectF.X); + rect->Y = gdip_round(rectF.Y); + rect->Width = gdip_round(rectF.Width); + rect->Height = gdip_round(rectF.Height); } return ret; diff --git a/reactos/dll/win32/gdiplus/font.c b/reactos/dll/win32/gdiplus/font.c index 4b059a0061f..6e313314017 100644 --- a/reactos/dll/win32/gdiplus/font.c +++ b/reactos/dll/win32/gdiplus/font.c @@ -116,41 +116,10 @@ typedef struct #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2') #define MS_HHEA_TAG MS_MAKE_TAG('h','h','e','a') -static const REAL mm_per_inch = 25.4; -static const REAL inch_per_point = 1.0/72.0; +static GpStatus clone_font_family(const GpFontFamily *, GpFontFamily **); static GpFontCollection installedFontCollection = {0}; -static LONG em_size_to_pixel(REAL em_size, Unit unit, LONG dpi) -{ - switch (unit) - { - default: - FIXME("Unhandled unit type: %d\n", unit); - return 0; - - case UnitPixel: - case UnitWorld: - /* FIXME: Figure out when World != Pixel */ - return em_size; - case UnitDisplay: - FIXME("Unknown behavior for UnitDisplay! Please report!\n"); - /* FIXME: Figure out how this works... - * MSDN says that if "DISPLAY" is a monitor, then pixel should be - * used. That's not what I got. Tests on Windows revealed no output, - * and the tests in tests/font crash windows */ - return 0; - case UnitPoint: - return em_size * dpi * inch_per_point; - case UnitInch: - return em_size * dpi; - case UnitDocument: - return em_size * dpi / 300.0; /* Per MSDN */ - case UnitMillimeter: - return em_size * dpi / mm_per_inch; - } -} - /******************************************************************************* * GdipCreateFont [GDIPLUS.@] * @@ -193,7 +162,7 @@ GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily, stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL); if (stat != Ok) return stat; - lfw.lfHeight = -em_size_to_pixel(emSize, unit, fontFamily->dpi); + lfw.lfHeight = -units_to_pixels(emSize, unit, fontFamily->dpi); lfw.lfWeight = style & FontStyleBold ? FW_BOLD : FW_REGULAR; lfw.lfItalic = style & FontStyleItalic; lfw.lfUnderline = style & FontStyleUnderline; @@ -216,7 +185,7 @@ GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily, (*font)->emSize = emSize; (*font)->otm = otm; - stat = GdipCloneFontFamily((GpFontFamily *)fontFamily, &(*font)->family); + stat = clone_font_family(fontFamily, &(*font)->family); if (stat != Ok) { GdipFree(*font); @@ -236,6 +205,7 @@ GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc, { HFONT hfont, oldfont; OUTLINETEXTMETRICW otm; + WCHAR facename[LF_FACESIZE]; GpStatus stat; int ret; @@ -248,6 +218,7 @@ GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc, oldfont = SelectObject(hdc, hfont); otm.otmSize = sizeof(otm); ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm); + GetTextFaceW(hdc, LF_FACESIZE, facename); SelectObject(hdc, oldfont); DeleteObject(hfont); @@ -260,7 +231,7 @@ GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc, (*font)->emSize = otm.otmTextMetrics.tmAscent; (*font)->otm = otm; - stat = GdipCreateFontFamilyFromName(logfont->lfFaceName, NULL, &(*font)->family); + stat = GdipCreateFontFamilyFromName(facename, NULL, &(*font)->family); if (stat != Ok) { GdipFree(*font); @@ -355,6 +326,11 @@ GpStatus WINGDIPAPI GdipGetFamily(GpFont *font, GpFontFamily **family) return GdipCloneFontFamily(font->family, family); } +static REAL get_font_size(const GpFont *font) +{ + return font->emSize; +} + /****************************************************************************** * GdipGetFontSize [GDIPLUS.@] * @@ -377,12 +353,30 @@ GpStatus WINGDIPAPI GdipGetFontSize(GpFont *font, REAL *size) if (!(font && size)) return InvalidParameter; - *size = font->emSize; + *size = get_font_size(font); TRACE("%s,%d => %f\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *size); return Ok; } +static INT get_font_style(const GpFont *font) +{ + INT style; + + if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR) + style = FontStyleBold; + else + style = FontStyleRegular; + if (font->otm.otmTextMetrics.tmItalic) + style |= FontStyleItalic; + if (font->otm.otmTextMetrics.tmUnderlined) + style |= FontStyleUnderline; + if (font->otm.otmTextMetrics.tmStruckOut) + style |= FontStyleStrikeout; + + return style; +} + /******************************************************************************* * GdipGetFontStyle [GDIPLUS.@] * @@ -403,16 +397,8 @@ GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style) if (!(font && style)) return InvalidParameter; - if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR) - *style = FontStyleBold; - else - *style = FontStyleRegular; - if (font->otm.otmTextMetrics.tmItalic) - *style |= FontStyleItalic; - if (font->otm.otmTextMetrics.tmUnderlined) - *style |= FontStyleUnderline; - if (font->otm.otmTextMetrics.tmStruckOut) - *style |= FontStyleStrikeout; + *style = get_font_style(font); + TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *style); return Ok; } @@ -466,31 +452,64 @@ GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics, /******************************************************************************* * GdipGetLogFontW [GDIPLUS.@] */ -GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics, - LOGFONTW *lfw) +GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics, LOGFONTW *lf) { - TRACE("(%p, %p, %p)\n", font, graphics, lfw); + REAL angle, rel_height, height; + GpMatrix matrix; + GpPointF pt[3]; - /* FIXME: use graphics */ - if(!font || !graphics || !lfw) + TRACE("(%p, %p, %p)\n", font, graphics, lf); + + if (!font || !graphics || !lf) return InvalidParameter; - lfw->lfHeight = -font->otm.otmTextMetrics.tmAscent; - lfw->lfWidth = 0; - lfw->lfEscapement = 0; - lfw->lfOrientation = 0; - lfw->lfWeight = font->otm.otmTextMetrics.tmWeight; - lfw->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0; - lfw->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0; - lfw->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0; - lfw->lfCharSet = font->otm.otmTextMetrics.tmCharSet; - lfw->lfOutPrecision = OUT_DEFAULT_PRECIS; - lfw->lfClipPrecision = CLIP_DEFAULT_PRECIS; - lfw->lfQuality = DEFAULT_QUALITY; - lfw->lfPitchAndFamily = 0; - strcpyW(lfw->lfFaceName, font->family->FamilyName); + matrix = graphics->worldtrans; - TRACE("=> %s,%d\n", debugstr_w(lfw->lfFaceName), lfw->lfHeight); + if (font->unit == UnitPixel) + { + height = units_to_pixels(font->emSize, graphics->unit, graphics->yres); + if (graphics->unit != UnitDisplay) + GdipScaleMatrix(&matrix, graphics->scale, graphics->scale, MatrixOrderAppend); + } + else + { + if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel) + height = units_to_pixels(font->emSize, font->unit, graphics->xres); + else + height = units_to_pixels(font->emSize, font->unit, graphics->yres); + } + + 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; + GdipTransformMatrixPoints(&matrix, pt, 3); + angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (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)); + + lf->lfHeight = -gdip_round(height * rel_height); + lf->lfWidth = 0; + lf->lfEscapement = lf->lfOrientation = gdip_round((angle / M_PI) * 1800.0); + if (lf->lfEscapement < 0) + { + lf->lfEscapement += 3600; + lf->lfOrientation += 3600; + } + lf->lfWeight = font->otm.otmTextMetrics.tmWeight; + lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0; + lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0; + lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0; + lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet; + lf->lfOutPrecision = OUT_DEFAULT_PRECIS; + lf->lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf->lfQuality = DEFAULT_QUALITY; + lf->lfPitchAndFamily = 0; + strcpyW(lf->lfFaceName, font->family->FamilyName); + + TRACE("=> %s,%d\n", debugstr_w(lf->lfFaceName), lf->lfHeight); return Ok; } @@ -535,18 +554,29 @@ GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font, { REAL dpi; GpStatus stat; + REAL font_height; TRACE("%p %p %p\n", font, graphics, height); - if (graphics) - { - stat = GdipGetDpiY((GpGraphics*)graphics, &dpi); - if (stat != Ok) return stat; - } - else - dpi = font->family->dpi; + stat = GdipGetFontHeightGivenDPI(font, font->family->dpi, &font_height); + if (stat != Ok) return stat; - return GdipGetFontHeightGivenDPI(font, dpi, height); + if (!graphics) + { + *height = font_height; + TRACE("%s,%d => %f\n", + debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height); + return Ok; + } + + stat = GdipGetDpiY((GpGraphics *)graphics, &dpi); + if (stat != Ok) return stat; + + *height = pixels_to_units(font_height, graphics->unit, dpi); + + TRACE("%s,%d(unit %d) => %f\n", + debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, graphics->unit, *height); + return Ok; } /******************************************************************************* @@ -569,49 +599,24 @@ GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, GpStatus stat; INT style; UINT16 line_spacing, em_height; - REAL font_height, font_size; + REAL font_size; if (!font || !height) return InvalidParameter; TRACE("%p (%s), %f, %p\n", font, debugstr_w(font->family->FamilyName), dpi, height); - stat = GdipGetFontSize((GpFont *)font, &font_size); - if (stat != Ok) return stat; - stat = GdipGetFontStyle((GpFont *)font, &style); - if (stat != Ok) return stat; + font_size = units_to_pixels(get_font_size(font), font->unit, dpi); + style = get_font_style(font); stat = GdipGetLineSpacing(font->family, style, &line_spacing); if (stat != Ok) return stat; stat = GdipGetEmHeight(font->family, style, &em_height); if (stat != Ok) return stat; - font_height = (REAL)line_spacing * font_size / (REAL)em_height; + *height = (REAL)line_spacing * font_size / (REAL)em_height; - switch (font->unit) - { - case UnitPixel: - case UnitWorld: - *height = font_height; - break; - case UnitPoint: - *height = font_height * dpi * inch_per_point; - break; - case UnitInch: - *height = font_height * dpi; - break; - case UnitDocument: - *height = font_height * (dpi / 300.0); - break; - case UnitMillimeter: - *height = font_height * (dpi / mm_per_inch); - break; - default: - FIXME("Unhandled unit type: %d\n", font->unit); - return NotImplemented; - } - - TRACE("%s,%d(unit %d) => %f\n", - debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, font->unit, *height); + TRACE("%s,%d => %f\n", + debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height); return Ok; } @@ -625,7 +630,7 @@ GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam) { - if (type != TRUETYPE_FONTTYPE) + if (type & RASTER_FONTTYPE) return 1; *(LOGFONTW *)lParam = *elf; @@ -635,6 +640,7 @@ static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf, struct font_metrics { + WCHAR facename[LF_FACESIZE]; UINT16 em_height, ascent, descent, line_spacing; /* in font units */ int dpi; }; @@ -650,6 +656,8 @@ static BOOL get_font_metrics(HDC hdc, struct font_metrics *fm) otm.otmSize = sizeof(otm); if (!GetOutlineTextMetricsW(hdc, otm.otmSize, &otm)) return FALSE; + GetTextFaceW(hdc, LF_FACESIZE, fm->facename); + fm->em_height = otm.otmEMSquare; fm->dpi = GetDeviceCaps(hdc, LOGPIXELSY); @@ -750,7 +758,7 @@ GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name, ffamily = GdipAlloc(sizeof (GpFontFamily)); if (!ffamily) return OutOfMemory; - lstrcpynW(ffamily->FamilyName, name, LF_FACESIZE); + lstrcpyW(ffamily->FamilyName, fm.facename); ffamily->em_height = fm.em_height; ffamily->ascent = fm.ascent; ffamily->descent = fm.descent; @@ -764,6 +772,16 @@ GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name, return Ok; } +static GpStatus clone_font_family(const GpFontFamily *family, GpFontFamily **clone) +{ + *clone = GdipAlloc(sizeof(GpFontFamily)); + if (!*clone) return OutOfMemory; + + **clone = *family; + + return Ok; +} + /******************************************************************************* * GdipCloneFontFamily [GDIPLUS.@] * @@ -778,16 +796,15 @@ GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name, */ GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily** clonedFontFamily) { + GpStatus status; + if (!(FontFamily && clonedFontFamily)) return InvalidParameter; TRACE("%p (%s), %p\n", FontFamily, debugstr_w(FontFamily->FamilyName), clonedFontFamily); - *clonedFontFamily = GdipAlloc(sizeof(GpFontFamily)); - if (!*clonedFontFamily) return OutOfMemory; - - **clonedFontFamily = *FontFamily; - lstrcpyW((*clonedFontFamily)->FamilyName, FontFamily->FamilyName); + status = clone_font_family(FontFamily, clonedFontFamily); + if (status != Ok) return status; TRACE("<-- %p\n", *clonedFontFamily); diff --git a/reactos/dll/win32/gdiplus/gdiplus.c b/reactos/dll/win32/gdiplus/gdiplus.c index 56d83304f36..1c016be27f7 100644 --- a/reactos/dll/win32/gdiplus/gdiplus.c +++ b/reactos/dll/win32/gdiplus/gdiplus.c @@ -35,6 +35,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); +static const REAL mm_per_inch = 25.4; +static const REAL point_per_inch = 72.0; + static Status WINAPI NotificationHook(ULONG_PTR *token) { TRACE("%p\n", token); @@ -207,7 +210,7 @@ static void unstretch_angle(REAL * angle, REAL rad_x, REAL rad_y) return; stretched = gdiplus_atan2(sin(*angle) / fabs(rad_y), cos(*angle) / fabs(rad_x)); - revs_off = roundr(*angle / (2.0 * M_PI)) - roundr(stretched / (2.0 * M_PI)); + revs_off = gdip_round(*angle / (2.0 * M_PI)) - gdip_round(stretched / (2.0 * M_PI)); stretched += ((REAL)revs_off) * M_PI * 2.0; *angle = stretched; } @@ -318,28 +321,57 @@ GpStatus hresult_to_status(HRESULT res) } /* converts a given unit to its value in pixels */ -REAL convert_unit(REAL logpixels, GpUnit unit) +REAL units_to_pixels(REAL units, GpUnit unit, REAL dpi) { - switch(unit) + switch (unit) { - case UnitInch: - return logpixels; - case UnitPoint: - return logpixels / 72.0; - case UnitDocument: - return logpixels / 300.0; - case UnitMillimeter: - return logpixels / 25.4; - case UnitWorld: - ERR("cannot convert UnitWorld\n"); - return 0.0; - case UnitPixel: - case UnitDisplay: - default: - return 1.0; + case UnitPixel: + case UnitWorld: + case UnitDisplay: + return units; + case UnitPoint: + return units * dpi / point_per_inch; + case UnitInch: + return units * dpi; + case UnitDocument: + return units * dpi / 300.0; /* Per MSDN */ + case UnitMillimeter: + return units * dpi / mm_per_inch; + default: + FIXME("Unhandled unit type: %d\n", unit); + return 0; } } +/* converts value in pixels to a given unit */ +REAL pixels_to_units(REAL pixels, GpUnit unit, REAL dpi) +{ + switch (unit) + { + case UnitPixel: + case UnitWorld: + case UnitDisplay: + return pixels; + case UnitPoint: + return pixels * point_per_inch / dpi; + case UnitInch: + return pixels / dpi; + case UnitDocument: + return pixels * 300.0 / dpi; + case UnitMillimeter: + return pixels * mm_per_inch / dpi; + default: + FIXME("Unhandled unit type: %d\n", unit); + return 0; + } +} + +REAL units_scale(GpUnit from, GpUnit to, REAL dpi) +{ + REAL pixels = units_to_pixels(1.0, from, dpi); + return pixels_to_units(pixels, to, dpi); +} + /* Calculates Bezier points from cardinal spline points. */ void calc_curve_bezier(CONST GpPointF *pts, REAL tension, REAL *x1, REAL *y1, REAL *x2, REAL *y2) @@ -362,8 +394,8 @@ void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj, REAL tension, REAL *x, REAL *y) { /* tangent at endpoints is the line from the endpoint to the adjacent point */ - *x = roundr(tension * (xadj - xend) + xend); - *y = roundr(tension * (yadj - yend) + yend); + *x = gdip_round(tension * (xadj - xend) + xend); + *y = gdip_round(tension * (yadj - yend) + yend); } /* make sure path has enough space for len more points */ diff --git a/reactos/dll/win32/gdiplus/gdiplus.spec b/reactos/dll/win32/gdiplus/gdiplus.spec index 01e8dd71ac1..ccf47f9f15d 100644 --- a/reactos/dll/win32/gdiplus/gdiplus.spec +++ b/reactos/dll/win32/gdiplus/gdiplus.spec @@ -471,10 +471,10 @@ 471 stdcall GdipRotatePenTransform(ptr float long) 472 stdcall GdipRotateTextureTransform(ptr float long) 473 stdcall GdipRotateWorldTransform(ptr float long) -474 stub GdipSaveAdd +474 stdcall GdipSaveAdd(ptr ptr) 475 stub GdipSaveAddImage 476 stdcall GdipSaveGraphics(ptr ptr) -477 stdcall GdipSaveImageToFile(ptr ptr ptr ptr) +477 stdcall GdipSaveImageToFile(ptr wstr ptr ptr) 478 stdcall GdipSaveImageToStream(ptr ptr ptr ptr) 479 stdcall GdipScaleLineTransform(ptr float float long) 480 stdcall GdipScaleMatrix(ptr float float long) diff --git a/reactos/dll/win32/gdiplus/gdiplus_private.h b/reactos/dll/win32/gdiplus/gdiplus_private.h index 1adfe0989f3..cb7330ef88a 100644 --- a/reactos/dll/win32/gdiplus/gdiplus_private.h +++ b/reactos/dll/win32/gdiplus/gdiplus_private.h @@ -29,6 +29,7 @@ #include "objbase.h" #include "ocidl.h" +#include "wincodecsdk.h" #include "wine/list.h" #include "gdiplus.h" @@ -47,7 +48,9 @@ extern INT arc2polybezier(GpPointF * points, REAL x1, REAL y1, REAL x2, REAL y2, REAL startAngle, REAL sweepAngle) DECLSPEC_HIDDEN; extern REAL gdiplus_atan2(REAL dy, REAL dx) DECLSPEC_HIDDEN; extern GpStatus hresult_to_status(HRESULT res) DECLSPEC_HIDDEN; -extern REAL convert_unit(REAL logpixels, GpUnit unit) DECLSPEC_HIDDEN; +extern REAL units_to_pixels(REAL units, GpUnit unit, REAL dpi) DECLSPEC_HIDDEN; +extern REAL pixels_to_units(REAL pixels, GpUnit unit, REAL dpi) DECLSPEC_HIDDEN; +extern REAL units_scale(GpUnit from, GpUnit to, REAL dpi) DECLSPEC_HIDDEN; extern GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics) DECLSPEC_HIDDEN; @@ -55,6 +58,7 @@ extern GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **r extern GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc) DECLSPEC_HIDDEN; extern GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc) DECLSPEC_HIDDEN; extern GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile) DECLSPEC_HIDDEN; +extern MetafileType METAFILE_GetEmfType(HENHMETAFILE hemf) DECLSPEC_HIDDEN; extern void calc_curve_bezier(CONST GpPointF *pts, REAL tension, REAL *x1, REAL *y1, REAL *x2, REAL *y2) DECLSPEC_HIDDEN; @@ -63,8 +67,6 @@ extern void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj, extern void free_installed_fonts(void) DECLSPEC_HIDDEN; -extern void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, HFONT *hfont) DECLSPEC_HIDDEN; - extern BOOL lengthen_path(GpPath *path, INT len) DECLSPEC_HIDDEN; extern GpStatus trace_path(GpGraphics *graphics, GpPath *path) DECLSPEC_HIDDEN; @@ -74,7 +76,7 @@ extern void delete_element(region_element *element) DECLSPEC_HIDDEN; extern GpStatus get_hatch_data(HatchStyle hatchstyle, const char **result) DECLSPEC_HIDDEN; -static inline INT roundr(REAL x) +static inline INT gdip_round(REAL x) { return (INT) floorf(x + 0.5); } @@ -121,7 +123,11 @@ extern void convert_32bppARGB_to_32bppPARGB(UINT width, UINT height, extern GpStatus convert_pixels(INT width, INT height, INT dst_stride, BYTE *dst_bits, PixelFormat dst_format, - INT src_stride, const BYTE *src_bits, PixelFormat src_format, ARGB *src_palette) DECLSPEC_HIDDEN; + INT src_stride, const BYTE *src_bits, PixelFormat src_format, ColorPalette *palette) DECLSPEC_HIDDEN; + +struct GpMatrix{ + REAL matrix[6]; +}; struct GpPen{ UINT style; @@ -146,6 +152,7 @@ struct GpGraphics{ HDC hdc; HWND hwnd; BOOL owndc; + BOOL alpha_hdc; GpImage *image; SmoothingMode smoothing; CompositingQuality compqual; @@ -155,7 +162,8 @@ struct GpGraphics{ TextRenderingHint texthint; GpUnit unit; /* page unit */ REAL scale; /* page scale */ - GpMatrix * worldtrans; /* world transform */ + REAL xres, yres; + GpMatrix worldtrans; /* world transform */ BOOL busy; /* hdc handle obtained by GdipGetDC */ GpRegion *clip; UINT textcontrast; /* not used yet. get/set only */ @@ -202,7 +210,7 @@ struct GpPathGradient{ ARGB* pblendcolor; /* preset blend colors */ REAL* pblendpos; /* preset blend positions */ INT pblendcount; - GpMatrix *transform; + GpMatrix transform; }; struct GpLineGradient{ @@ -224,7 +232,7 @@ struct GpLineGradient{ struct GpTexture{ GpBrush brush; - GpMatrix *transform; + GpMatrix transform; GpImage *image; GpImageAttributes *imageattributes; BYTE *bitmap_bits; /* image bits converted to ARGB and run through imageattributes */ @@ -237,10 +245,6 @@ struct GpPath{ INT datalen; /* size of the arrays in pathdata */ }; -struct GpMatrix{ - REAL matrix[6]; -}; - struct GpPathIterator{ GpPathData pathdata; INT subpath_pos; /* for NextSubpath methods */ @@ -262,14 +266,13 @@ struct GpAdustableArrowCap{ }; struct GpImage{ - IPicture* picture; + IPicture *picture; + IStream *stream; /* source stream */ ImageType type; GUID format; UINT flags; - UINT palette_flags; - UINT palette_count; - UINT palette_size; - ARGB *palette_entries; + UINT frame_count, current_frame; + ColorPalette *palette; REAL xres, yres; }; @@ -279,6 +282,7 @@ struct GpMetafile{ GpUnit unit; MetafileType metafile_type; HENHMETAFILE hemf; + int preserve_hemf; /* if true, hemf belongs to the app and should not be deleted */ /* recording */ HDC record_dc; @@ -309,6 +313,9 @@ struct GpBitmap{ INT stride; /* stride of bits if this is a DIB */ BYTE *own_bits; /* image bits that need to be freed with this object */ INT lockx, locky; /* X and Y coordinates of the rect when a bitmap is locked for writing. */ + IWICMetadataReader *metadata_reader; /* NULL if there is no metadata */ + UINT prop_count; + PropertyItem *prop_item; /* cached image properties */ }; struct GpCachedBitmap{ @@ -366,6 +373,7 @@ struct GpStringFormat{ REAL *tabs; CharacterRange *character_ranges; INT range_count; + BOOL generic_typographic; }; struct GpFontCollection{ @@ -436,4 +444,6 @@ GpStatus gdip_format_string(HDC hdc, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, gdip_format_string_callback callback, void *user_data) DECLSPEC_HIDDEN; +void get_log_fontW(const GpFont *, GpGraphics *, LOGFONTW *) DECLSPEC_HIDDEN; + #endif diff --git a/reactos/dll/win32/gdiplus/graphics.c b/reactos/dll/win32/gdiplus/graphics.c index 3881ec8492f..e9b21eb12e9 100644 --- a/reactos/dll/win32/gdiplus/graphics.c +++ b/reactos/dll/win32/gdiplus/graphics.c @@ -63,6 +63,11 @@ static volatile LONG g_priv_contid = GDIP_CONTID_STEP; #define ANCHOR_WIDTH (2.0) #define MAX_ITERS (50) +static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length, + GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, + GDIPCONST GpBrush *brush, GDIPCONST PointF *positions, + INT flags, GDIPCONST GpMatrix *matrix); + /* Converts angle (in degrees) to x/y coordinates */ static void deg2xy(REAL angle, REAL x_0, REAL y_0, REAL *x, REAL *y) { @@ -101,12 +106,6 @@ static BYTE convert_path_point_type(BYTE type) return ret; } -static REAL graphics_res(GpGraphics *graphics) -{ - if (graphics->image) return graphics->image->xres; - else return (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX); -} - static COLORREF get_gdi_brush_color(const GpBrush *brush) { ARGB argb; @@ -273,12 +272,11 @@ static INT prepare_dc(GpGraphics *graphics, GpPen *pen) pt[0].Y = 0.0; pt[1].X = 1.0; pt[1].Y = 1.0; - GdipTransformMatrixPoints(graphics->worldtrans, pt, 2); + GdipTransformMatrixPoints(&graphics->worldtrans, pt, 2); width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) + (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0); - width *= pen->width * convert_unit(graphics_res(graphics), - pen->unit == UnitWorld ? graphics->unit : pen->unit); + width *= units_to_pixels(pen->width, pen->unit == UnitWorld ? graphics->unit : pen->unit, graphics->xres); } if(pen->dash == DashStyleCustom){ @@ -286,20 +284,20 @@ static INT prepare_dc(GpGraphics *graphics, GpPen *pen) TRACE("dashes are: "); for(i = 0; i < numdashes; i++){ - dash_array[i] = roundr(width * pen->dashes[i]); + dash_array[i] = gdip_round(width * pen->dashes[i]); TRACE("%d, ", dash_array[i]); } TRACE("\n and the pen style is %x\n", pen->style); create_gdi_logbrush(pen->brush, &lb); - gdipen = ExtCreatePen(pen->style, roundr(width), &lb, + gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb, numdashes, dash_array); free_gdi_logbrush(&lb); } else { create_gdi_logbrush(pen->brush, &lb); - gdipen = ExtCreatePen(pen->style, roundr(width), &lb, 0, NULL); + gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb, 0, NULL); free_gdi_logbrush(&lb); } @@ -315,7 +313,7 @@ static void restore_dc(GpGraphics *graphics, INT state) } static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space, - GpCoordinateSpace src_space, GpMatrix **matrix); + GpCoordinateSpace src_space, GpMatrix *matrix); /* This helper applies all the changes that the points listed in ptf need in * order to be drawn on the device context. In the end, this should include at @@ -330,24 +328,27 @@ static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace d static void transform_and_round_points(GpGraphics *graphics, POINT *pti, GpPointF *ptf, INT count) { - REAL unitscale; - GpMatrix *matrix; + REAL scale_x, scale_y; + GpMatrix matrix; int i; - unitscale = convert_unit(graphics_res(graphics), graphics->unit); + scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres); + scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres); /* apply page scale */ if(graphics->unit != UnitDisplay) - unitscale *= graphics->scale; + { + scale_x *= graphics->scale; + scale_y *= graphics->scale; + } - GdipCloneMatrix(graphics->worldtrans, &matrix); - GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend); - GdipTransformMatrixPoints(matrix, ptf, count); - GdipDeleteMatrix(matrix); + matrix = graphics->worldtrans; + GdipScaleMatrix(&matrix, scale_x, scale_y, MatrixOrderAppend); + GdipTransformMatrixPoints(&matrix, ptf, count); for(i = 0; i < count; i++){ - pti[i].x = roundr(ptf[i].X); - pti[i].y = roundr(ptf[i].Y); + pti[i].x = gdip_round(ptf[i].X); + pti[i].y = gdip_round(ptf[i].Y); } } @@ -375,68 +376,67 @@ static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_ } } +static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn) +{ + return GdipGetRegionHRgn(graphics->clip, graphics, hrgn); +} + /* Draw non-premultiplied ARGB data to the given graphics object */ -static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y, +static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_y, const BYTE *src, INT src_width, INT src_height, INT src_stride) { - if (graphics->image && graphics->image->type == ImageTypeBitmap) - { - GpBitmap *dst_bitmap = (GpBitmap*)graphics->image; - INT x, y; + GpBitmap *dst_bitmap = (GpBitmap*)graphics->image; + INT x, y; - for (x=0; ximage && graphics->image->type == ImageTypeMetafile) - { - ERR("This should not be used for metafiles; fix caller\n"); - return NotImplemented; - } - else - { - HDC hdc; - HBITMAP hbitmap; - BITMAPINFOHEADER bih; - BYTE *temp_bits; - hdc = CreateCompatibleDC(0); + return Ok; +} - bih.biSize = sizeof(BITMAPINFOHEADER); - bih.biWidth = src_width; - bih.biHeight = -src_height; - bih.biPlanes = 1; - bih.biBitCount = 32; - bih.biCompression = BI_RGB; - bih.biSizeImage = 0; - bih.biXPelsPerMeter = 0; - bih.biYPelsPerMeter = 0; - bih.biClrUsed = 0; - bih.biClrImportant = 0; +static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_y, + const BYTE *src, INT src_width, INT src_height, INT src_stride) +{ + HDC hdc; + HBITMAP hbitmap; + BITMAPINFOHEADER bih; + BYTE *temp_bits; - hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS, - (void**)&temp_bits, NULL, 0); + hdc = CreateCompatibleDC(0); - convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits, - 4 * src_width, src, src_stride); + bih.biSize = sizeof(BITMAPINFOHEADER); + bih.biWidth = src_width; + bih.biHeight = -src_height; + bih.biPlanes = 1; + bih.biBitCount = 32; + bih.biCompression = BI_RGB; + bih.biSizeImage = 0; + bih.biXPelsPerMeter = 0; + bih.biYPelsPerMeter = 0; + bih.biClrUsed = 0; + bih.biClrImportant = 0; - SelectObject(hdc, hbitmap); - gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height, - hdc, 0, 0, src_width, src_height); - DeleteDC(hdc); - DeleteObject(hbitmap); + hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS, + (void**)&temp_bits, NULL, 0); - return Ok; - } + convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits, + 4 * src_width, src, src_stride); + + SelectObject(hdc, hbitmap); + gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height, + hdc, 0, 0, src_width, src_height); + DeleteDC(hdc); + DeleteObject(hbitmap); + + return Ok; } static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y, @@ -449,20 +449,44 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst int i, size; RGNDATA *rgndata; RECT *rects; + HRGN hrgn, visible_rgn; - size = GetRegionData(hregion, 0, NULL); + hrgn = CreateRectRgn(dst_x, dst_y, dst_x + src_width, dst_y + src_height); + if (!hrgn) + return OutOfMemory; + + stat = get_clip_hrgn(graphics, &visible_rgn); + if (stat != Ok) + { + DeleteObject(hrgn); + return stat; + } + + if (visible_rgn) + { + CombineRgn(hrgn, hrgn, visible_rgn, RGN_AND); + DeleteObject(visible_rgn); + } + + if (hregion) + CombineRgn(hrgn, hrgn, hregion, RGN_AND); + + size = GetRegionData(hrgn, 0, NULL); rgndata = GdipAlloc(size); if (!rgndata) + { + DeleteObject(hrgn); return OutOfMemory; + } - GetRegionData(hregion, size, rgndata); + GetRegionData(hrgn, size, rgndata); - rects = (RECT*)&rgndata->Buffer; + rects = (RECT*)rgndata->Buffer; for (i=0; stat == Ok && irdh.nCount; i++) { - stat = alpha_blend_pixels(graphics, rects[i].left, rects[i].top, + stat = alpha_blend_bmp_pixels(graphics, rects[i].left, rects[i].top, &src[(rects[i].left - dst_x) * 4 + (rects[i].top - dst_y) * src_stride], rects[i].right - rects[i].left, rects[i].bottom - rects[i].top, src_stride); @@ -470,6 +494,8 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst GdipFree(rgndata); + DeleteObject(hrgn); + return stat; } else if (graphics->image && graphics->image->type == ImageTypeMetafile) @@ -479,21 +505,39 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst } else { + HRGN hrgn; int save; + stat = get_clip_hrgn(graphics, &hrgn); + + if (stat != Ok) + return stat; + save = SaveDC(graphics->hdc); - ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND); + if (hrgn) + ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND); - stat = alpha_blend_pixels(graphics, dst_x, dst_y, src, src_width, + if (hregion) + ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND); + + stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width, src_height, src_stride); RestoreDC(graphics->hdc, save); + DeleteObject(hrgn); + return stat; } } +static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y, + const BYTE *src, INT src_width, INT src_height, INT src_stride) +{ + return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL); +} + static ARGB blend_colors(ARGB start, ARGB end, REAL position) { ARGB result=0; @@ -759,10 +803,10 @@ static void get_bitmap_sample_size(InterpolationMode interpolation, WrapMode wra break; case InterpolationModeNearestNeighbor: default: - left = roundr(srcx); - top = roundr(srcy); - right = roundr(srcx+srcwidth); - bottom = roundr(srcy+srcheight); + left = gdip_round(srcx); + top = gdip_round(srcy); + right = gdip_round(srcx+srcwidth); + bottom = gdip_round(srcy+srcheight); break; } @@ -851,7 +895,7 @@ static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT wi static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width, UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes, - InterpolationMode interpolation) + InterpolationMode interpolation, PixelOffsetMode offset_mode) { static int fixme; @@ -896,8 +940,25 @@ static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT return blend_colors(top, bottom, point->Y - topyf); } case InterpolationModeNearestNeighbor: + { + FLOAT pixel_offset; + switch (offset_mode) + { + default: + case PixelOffsetModeNone: + case PixelOffsetModeHighSpeed: + pixel_offset = 0.5; + break; + + case PixelOffsetModeHalf: + case PixelOffsetModeHighQuality: + pixel_offset = 0.0; + break; + } return sample_bitmap_pixel(src_rect, bits, width, height, - roundr(point->X), roundr(point->Y), attributes); + floorf(point->X + pixel_offset), floorf(point->Y + pixel_offset), attributes); + } + } } @@ -1094,7 +1155,6 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, GpTexture *fill = (GpTexture*)brush; GpPointF draw_points[3]; GpStatus stat; - GpMatrix *world_to_texture; int x, y; GpBitmap *bitmap; int src_stride; @@ -1126,17 +1186,11 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, if (stat == Ok) { - stat = GdipCloneMatrix(fill->transform, &world_to_texture); - } - - if (stat == Ok) - { - stat = GdipInvertMatrix(world_to_texture); + GpMatrix world_to_texture = fill->transform; + stat = GdipInvertMatrix(&world_to_texture); if (stat == Ok) - stat = GdipTransformMatrixPoints(world_to_texture, draw_points, 3); - - GdipDeleteMatrix(world_to_texture); + stat = GdipTransformMatrixPoints(&world_to_texture, draw_points, 3); } if (stat == Ok && !fill->bitmap_bits) @@ -1191,7 +1245,8 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, argb_pixels[x + y*cdwStride] = resample_bitmap_pixel( &src_area, fill->bitmap_bits, bitmap->width, bitmap->height, - &point, fill->imageattributes, graphics->interpolation); + &point, fill->imageattributes, graphics->interpolation, + graphics->pixeloffset); } } } @@ -1202,7 +1257,7 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, { GpPathGradient *fill = (GpPathGradient*)brush; GpPath *flat_path; - GpMatrix *world_to_device; + GpMatrix world_to_device; GpStatus stat; int i, figure_start=0; GpPointF start_point, end_point, center_point; @@ -1244,7 +1299,7 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, if (!transform_fixme_once) { BOOL is_identity=TRUE; - GdipIsMatrixIdentity(fill->transform, &is_identity); + GdipIsMatrixIdentity(&fill->transform, &is_identity); if (!is_identity) { FIXME("path gradient transform not implemented\n"); @@ -1261,18 +1316,16 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, CoordinateSpaceWorld, &world_to_device); if (stat == Ok) { - stat = GdipTransformPath(flat_path, world_to_device); + stat = GdipTransformPath(flat_path, &world_to_device); if (stat == Ok) { center_point = fill->center; - stat = GdipTransformMatrixPoints(world_to_device, ¢er_point, 1); + stat = GdipTransformMatrixPoints(&world_to_device, ¢er_point, 1); } if (stat == Ok) stat = GdipFlattenPath(flat_path, NULL, 0.5); - - GdipDeleteMatrix(world_to_device); } if (stat != Ok) @@ -1446,7 +1499,7 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2) { HGDIOBJ oldbrush = NULL, oldpen = NULL; - GpMatrix *matrix = NULL; + GpMatrix matrix; HBRUSH brush = NULL; HPEN pen = NULL; PointF ptf[4], *custptf = NULL; @@ -1591,16 +1644,17 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s custpt = GdipAlloc(count * sizeof(POINT)); tp = GdipAlloc(count); - if(!custptf || !custpt || !tp || (GdipCreateMatrix(&matrix) != Ok)) + if(!custptf || !custpt || !tp) goto custend; memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF)); - GdipScaleMatrix(matrix, size, size, MatrixOrderAppend); - GdipRotateMatrix(matrix, (180.0 / M_PI) * (theta - M_PI_2), + GdipSetMatrixElements(&matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + GdipScaleMatrix(&matrix, size, size, MatrixOrderAppend); + GdipRotateMatrix(&matrix, (180.0 / M_PI) * (theta - M_PI_2), MatrixOrderAppend); - GdipTranslateMatrix(matrix, x2, y2, MatrixOrderAppend); - GdipTransformMatrixPoints(matrix, custptf, count); + GdipTranslateMatrix(&matrix, x2, y2, MatrixOrderAppend); + GdipTransformMatrixPoints(&matrix, custptf, count); transform_and_round_points(graphics, custpt, custptf, count); @@ -1620,7 +1674,6 @@ custend: GdipFree(custptf); GdipFree(custpt); GdipFree(tp); - GdipDeleteMatrix(matrix); break; default: break; @@ -1985,7 +2038,7 @@ typedef struct _GraphicsContainerItem { GpUnit unit; PixelOffsetMode pixeloffset; UINT textcontrast; - GpMatrix* worldtrans; + GpMatrix worldtrans; GpRegion* clip; INT origin_x, origin_y; } GraphicsContainerItem; @@ -2011,17 +2064,10 @@ static GpStatus init_container(GraphicsContainerItem** container, (*container)->pixeloffset = graphics->pixeloffset; (*container)->origin_x = graphics->origin_x; (*container)->origin_y = graphics->origin_y; - - sts = GdipCloneMatrix(graphics->worldtrans, &(*container)->worldtrans); - if(sts != Ok){ - GdipFree(*container); - *container = NULL; - return sts; - } + (*container)->worldtrans = graphics->worldtrans; sts = GdipCloneRegion(graphics->clip, &(*container)->clip); if(sts != Ok){ - GdipDeleteMatrix((*container)->worldtrans); GdipFree(*container); *container = NULL; return sts; @@ -2030,8 +2076,8 @@ static GpStatus init_container(GraphicsContainerItem** container, return Ok; } -static void delete_container(GraphicsContainerItem* container){ - GdipDeleteMatrix(container->worldtrans); +static void delete_container(GraphicsContainerItem* container) +{ GdipDeleteRegion(container->clip); GdipFree(container); } @@ -2039,21 +2085,12 @@ static void delete_container(GraphicsContainerItem* container){ static GpStatus restore_container(GpGraphics* graphics, GDIPCONST GraphicsContainerItem* container){ GpStatus sts; - GpMatrix *newTrans; GpRegion *newClip; - sts = GdipCloneMatrix(container->worldtrans, &newTrans); - if(sts != Ok) - return sts; - sts = GdipCloneRegion(container->clip, &newClip); - if(sts != Ok){ - GdipDeleteMatrix(newTrans); - return sts; - } + if(sts != Ok) return sts; - GdipDeleteMatrix(graphics->worldtrans); - graphics->worldtrans = newTrans; + graphics->worldtrans = container->worldtrans; GdipDeleteRegion(graphics->clip); graphics->clip = newClip; @@ -2093,6 +2130,25 @@ static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect) stat = GdipGetImageBounds(graphics->image, rect, &unit); if (stat == Ok && unit != UnitPixel) FIXME("need to convert from unit %i\n", unit); + }else if (GetObjectType(graphics->hdc) == OBJ_MEMDC){ + HBITMAP hbmp; + BITMAP bmp; + + rect->X = 0; + rect->Y = 0; + + hbmp = GetCurrentObject(graphics->hdc, OBJ_BITMAP); + if (hbmp && GetObjectW(hbmp, sizeof(bmp), &bmp)) + { + rect->Width = bmp.bmWidth; + rect->Height = bmp.bmHeight; + } + else + { + /* FIXME: ??? */ + rect->Width = 1; + rect->Height = 1; + } }else{ rect->X = 0; rect->Y = 0; @@ -2130,21 +2186,72 @@ end: return stat; } -void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, HFONT *hfont) +void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf) +{ + REAL height; + + if (font->unit == UnitPixel) + { + height = units_to_pixels(font->emSize, graphics->unit, graphics->yres); + } + else + { + if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel) + height = units_to_pixels(font->emSize, font->unit, graphics->xres); + else + height = units_to_pixels(font->emSize, font->unit, graphics->yres); + } + + lf->lfHeight = -(height + 0.5); + lf->lfWidth = 0; + lf->lfEscapement = 0; + lf->lfOrientation = 0; + lf->lfWeight = font->otm.otmTextMetrics.tmWeight; + lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0; + lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0; + lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0; + lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet; + lf->lfOutPrecision = OUT_DEFAULT_PRECIS; + lf->lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf->lfQuality = DEFAULT_QUALITY; + lf->lfPitchAndFamily = 0; + strcpyW(lf->lfFaceName, font->family->FamilyName); +} + +static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, + GDIPCONST GpStringFormat *format, HFONT *hfont, + GDIPCONST GpMatrix *matrix) { HDC hdc = CreateCompatibleDC(0); GpPointF pt[3]; - REAL angle, rel_width, rel_height; + REAL angle, rel_width, rel_height, font_height; LOGFONTW lfw; HFONT unscaled_font; TEXTMETRICW textmet; + if (font->unit == UnitPixel) + font_height = font->emSize; + else + { + REAL unit_scale, res; + + res = (graphics->unit == UnitDisplay || graphics->unit == UnitPixel) ? graphics->xres : graphics->yres; + unit_scale = units_scale(font->unit, graphics->unit, res); + + font_height = font->emSize * unit_scale; + } + 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; + if (matrix) + { + GpMatrix xform = *matrix; + GdipTransformMatrixPoints(&xform, pt, 3); + } if (graphics) GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3); angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X)); @@ -2153,15 +2260,15 @@ void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, HFONT *hfont) 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)); - GdipGetLogFontW((GpFont *)font, graphics, &lfw); - lfw.lfHeight = roundr(lfw.lfHeight * rel_height); + get_log_fontW(font, graphics, &lfw); + lfw.lfHeight = gdip_round(font_height * rel_height); unscaled_font = CreateFontIndirectW(&lfw); SelectObject(hdc, unscaled_font); GetTextMetricsW(hdc, &textmet); - lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width / rel_height); - lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0); + lfw.lfWidth = gdip_round(textmet.tmAveCharWidth * rel_width / rel_height); + lfw.lfEscapement = lfw.lfOrientation = gdip_round((angle / M_PI) * 1800.0); *hfont = CreateFontIndirectW(&lfw); @@ -2179,6 +2286,8 @@ GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics) GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics) { GpStatus retval; + HBITMAP hbitmap; + DIBSECTION dib; TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics); @@ -2196,15 +2305,18 @@ GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **gra *graphics = GdipAlloc(sizeof(GpGraphics)); if(!*graphics) return OutOfMemory; - if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){ + GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + + if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){ GdipFree(*graphics); return retval; } - if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){ - GdipFree((*graphics)->worldtrans); - GdipFree(*graphics); - return retval; + hbitmap = GetCurrentObject(hdc, OBJ_BITMAP); + if (hbitmap && GetObjectW(hbitmap, sizeof(dib), &dib) == sizeof(dib) && + dib.dsBmih.biBitCount == 32 && dib.dsBmih.biCompression == BI_RGB) + { + (*graphics)->alpha_hdc = 1; } (*graphics)->hdc = hdc; @@ -2217,6 +2329,8 @@ GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **gra (*graphics)->compmode = CompositingModeSourceOver; (*graphics)->unit = UnitDisplay; (*graphics)->scale = 1.0; + (*graphics)->xres = GetDeviceCaps(hdc, LOGPIXELSX); + (*graphics)->yres = GetDeviceCaps(hdc, LOGPIXELSY); (*graphics)->busy = FALSE; (*graphics)->textcontrast = 4; list_init(&(*graphics)->containers); @@ -2234,13 +2348,9 @@ GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics) *graphics = GdipAlloc(sizeof(GpGraphics)); if(!*graphics) return OutOfMemory; - if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){ - GdipFree(*graphics); - return retval; - } + GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){ - GdipFree((*graphics)->worldtrans); GdipFree(*graphics); return retval; } @@ -2256,6 +2366,8 @@ GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics) (*graphics)->compmode = CompositingModeSourceOver; (*graphics)->unit = UnitDisplay; (*graphics)->scale = 1.0; + (*graphics)->xres = image->xres; + (*graphics)->yres = image->yres; (*graphics)->busy = FALSE; (*graphics)->textcontrast = 4; list_init(&(*graphics)->containers); @@ -2298,65 +2410,43 @@ GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics) GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete, GpMetafile **metafile) { - IStream *stream = NULL; - UINT read; - ENHMETAHEADER *copy; - GpStatus retval = Ok; + ENHMETAHEADER header; + MetafileType metafile_type; TRACE("(%p,%i,%p)\n", hemf, delete, metafile); if(!hemf || !metafile) return InvalidParameter; - read = GetEnhMetaFileBits(hemf, 0, NULL); - copy = GdipAlloc(read); - GetEnhMetaFileBits(hemf, read, (BYTE *)copy); + if (GetEnhMetaFileHeader(hemf, sizeof(header), &header) == 0) + return GenericError; - if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){ - ERR("could not make stream\n"); - GdipFree(copy); - retval = GenericError; - goto err; - } + metafile_type = METAFILE_GetEmfType(hemf); + + if (metafile_type == MetafileTypeInvalid) + return GenericError; *metafile = GdipAlloc(sizeof(GpMetafile)); - if(!*metafile){ - retval = OutOfMemory; - goto err; - } - - if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture, - (LPVOID*) &((*metafile)->image.picture)) != S_OK) - { - retval = GenericError; - goto err; - } - + if (!*metafile) + return OutOfMemory; (*metafile)->image.type = ImageTypeMetafile; - memcpy(&(*metafile)->image.format, &ImageFormatWMF, sizeof(GUID)); - (*metafile)->image.palette_flags = 0; - (*metafile)->image.palette_count = 0; - (*metafile)->image.palette_size = 0; - (*metafile)->image.palette_entries = NULL; - (*metafile)->image.xres = (REAL)copy->szlDevice.cx; - (*metafile)->image.yres = (REAL)copy->szlDevice.cy; - (*metafile)->bounds.X = (REAL)copy->rclBounds.left; - (*metafile)->bounds.Y = (REAL)copy->rclBounds.top; - (*metafile)->bounds.Width = (REAL)(copy->rclBounds.right - copy->rclBounds.left); - (*metafile)->bounds.Height = (REAL)(copy->rclBounds.bottom - copy->rclBounds.top); + (*metafile)->image.format = ImageFormatEMF; + (*metafile)->image.frame_count = 1; + (*metafile)->image.xres = (REAL)header.szlDevice.cx; + (*metafile)->image.yres = (REAL)header.szlDevice.cy; + (*metafile)->bounds.X = (REAL)header.rclBounds.left; + (*metafile)->bounds.Y = (REAL)header.rclBounds.top; + (*metafile)->bounds.Width = (REAL)(header.rclBounds.right - header.rclBounds.left); + (*metafile)->bounds.Height = (REAL)(header.rclBounds.bottom - header.rclBounds.top); (*metafile)->unit = UnitPixel; - - if(delete) - DeleteEnhMetaFile(hemf); + (*metafile)->metafile_type = metafile_type; + (*metafile)->hemf = hemf; + (*metafile)->preserve_hemf = !delete; TRACE("<-- %p\n", *metafile); -err: - if (retval != Ok) - GdipFree(*metafile); - IStream_Release(stream); - return retval; + return Ok; } GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete, @@ -2382,7 +2472,8 @@ GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete, hemf = SetWinMetaFileBits(read, copy, NULL, NULL); GdipFree(copy); - retval = GdipCreateMetafileFromEmf(hemf, FALSE, metafile); + /* FIXME: We should store and use hwmf instead of converting to hemf */ + retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile); if (retval == Ok) { @@ -2394,9 +2485,13 @@ GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete, placeable->BoundingBox.Left); (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom - placeable->BoundingBox.Top); + (*metafile)->metafile_type = MetafileTypeWmfPlaceable; + (*metafile)->image.format = ImageFormatWMF; if (delete) DeleteMetaFile(hwmf); } + else + DeleteEnhMetaFile(hemf); return retval; } @@ -2474,7 +2569,6 @@ GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics) } GdipDeleteRegion(graphics->clip); - GdipDeleteMatrix(graphics->worldtrans); GdipFree(graphics); return Ok; @@ -2946,7 +3040,6 @@ GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x, GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y) { UINT width, height; - GpPointF points[3]; TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y); @@ -2956,15 +3049,8 @@ GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, GdipGetImageWidth(image, &width); GdipGetImageHeight(image, &height); - /* FIXME: we should use the graphics and image dpi, somehow */ - - points[0].X = points[2].X = x; - points[0].Y = points[1].Y = y; - points[1].X = x + width; - points[2].Y = y + height; - - return GdipDrawImagePointsRect(graphics, image, points, 3, 0, 0, width, height, - UnitPixel, NULL, NULL, NULL); + return GdipDrawImagePointRect(graphics, image, x, y, + 0.0, 0.0, (REAL)width, (REAL)height, UnitPixel); } GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x, @@ -2980,14 +3066,21 @@ GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image, GpUnit srcUnit) { GpPointF points[3]; + REAL scale_x, scale_y, width, height; + TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit); + scale_x = units_scale(srcUnit, graphics->unit, graphics->xres); + scale_x *= graphics->xres / image->xres; + scale_y = units_scale(srcUnit, graphics->unit, graphics->yres); + scale_y *= graphics->yres / image->yres; + width = srcwidth * scale_x; + height = srcheight * scale_y; + points[0].X = points[2].X = x; points[0].Y = points[1].Y = y; - - /* FIXME: convert image coordinates to Graphics coordinates? */ - points[1].X = x + srcwidth; - points[2].Y = y + srcheight; + points[1].X = x + width; + points[2].Y = y + height; return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy, srcwidth, srcheight, srcUnit, NULL, NULL, NULL); @@ -3037,6 +3130,13 @@ GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image, return GdipDrawImagePoints(graphics, image, ptf, count); } +static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned int flags, + unsigned int dataSize, const unsigned char *pStr, void *userdata) +{ + GdipPlayMetafileRecord(userdata, record_type, flags, dataSize, pStr); + return TRUE; +} + GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image, GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes, @@ -3044,7 +3144,6 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image { GpPointF ptf[4]; POINT pti[4]; - REAL dx, dy; GpStatus stat; TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points, @@ -3067,6 +3166,15 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image return Ok; transform_and_round_points(graphics, pti, ptf, 4); + TRACE("%s %s %s %s\n", wine_dbgstr_point(&pti[0]), wine_dbgstr_point(&pti[1]), + wine_dbgstr_point(&pti[2]), wine_dbgstr_point(&pti[3])); + + srcx = units_to_pixels(srcx, srcUnit, image->xres); + srcy = units_to_pixels(srcy, srcUnit, image->yres); + srcwidth = units_to_pixels(srcwidth, srcUnit, image->xres); + srcheight = units_to_pixels(srcheight, srcUnit, image->yres); + TRACE("src pixels: %f,%f %fx%f\n", srcx, srcy, srcwidth, srcheight); + if (image->picture) { if (!graphics->hdc) @@ -3074,23 +3182,10 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image FIXME("graphics object has no HDC\n"); } - /* FIXME: partially implemented (only works for rectangular parallelograms) */ - if(srcUnit == UnitInch) - dx = dy = (REAL) INCH_HIMETRIC; - else if(srcUnit == UnitPixel){ - dx = ((REAL) INCH_HIMETRIC) / - ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX)); - dy = ((REAL) INCH_HIMETRIC) / - ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY)); - } - else - return NotImplemented; - if(IPicture_Render(image->picture, graphics->hdc, pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y, - srcx * dx, srcy * dy, - srcwidth * dx, srcheight * dy, - NULL) != S_OK){ + srcx, srcy, srcwidth, srcheight, NULL) != S_OK) + { if(callback) callback(callbackData); return GenericError; @@ -3101,21 +3196,14 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image GpBitmap* bitmap = (GpBitmap*)image; int use_software=0; - if (srcUnit == UnitInch) - dx = dy = 96.0; /* FIXME: use the image resolution */ - else if (srcUnit == UnitPixel) - dx = dy = 1.0; - else - return NotImplemented; + TRACE("graphics: %.2fx%.2f dpi, fmt %#x, scale %f, image: %.2fx%.2f dpi, fmt %#x, color %08x\n", + graphics->xres, graphics->yres, + graphics->image && graphics->image->type == ImageTypeBitmap ? ((GpBitmap *)graphics->image)->format : 0, + graphics->scale, image->xres, image->yres, bitmap->format, + imageAttributes ? imageAttributes->outside_color : 0); - srcx = srcx * dx; - srcy = srcy * dy; - srcwidth = srcwidth * dx; - srcheight = srcheight * dy; - - if (imageAttributes || + if (imageAttributes || graphics->alpha_hdc || (graphics->image && graphics->image->type == ImageTypeBitmap) || - !((GpBitmap*)image)->hbitmap || ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X || ptf[1].X - ptf[0].X != srcwidth || ptf[2].Y - ptf[0].Y != srcheight || srcx < 0 || srcy < 0 || @@ -3127,11 +3215,12 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image RECT dst_area; GpRect src_area; int i, x, y, src_stride, dst_stride; - GpMatrix *dst_to_src; + GpMatrix dst_to_src; REAL m11, m12, m21, m22, mdx, mdy; LPBYTE src_data, dst_data; BitmapData lockeddata; InterpolationMode interpolation = graphics->interpolation; + PixelOffsetMode offset_mode = graphics->pixeloffset; GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; REAL x_dx, x_dy, y_dx, y_dy; static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE}; @@ -3149,6 +3238,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y; } + TRACE("dst_area: %s\n", wine_dbgstr_rect(&dst_area)); + m11 = (ptf[1].X - ptf[0].X) / srcwidth; m21 = (ptf[2].X - ptf[0].X) / srcheight; mdx = ptf[0].X - m11 * srcx - m21 * srcy; @@ -3156,33 +3247,25 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image m22 = (ptf[2].Y - ptf[0].Y) / srcheight; mdy = ptf[0].Y - m12 * srcx - m22 * srcy; - stat = GdipCreateMatrix2(m11, m12, m21, m22, mdx, mdy, &dst_to_src); + GdipSetMatrixElements(&dst_to_src, m11, m12, m21, m22, mdx, mdy); + + stat = GdipInvertMatrix(&dst_to_src); if (stat != Ok) return stat; - stat = GdipInvertMatrix(dst_to_src); - if (stat != Ok) - { - GdipDeleteMatrix(dst_to_src); - return stat; - } - dst_data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top)); - if (!dst_data) - { - GdipDeleteMatrix(dst_to_src); - return OutOfMemory; - } + if (!dst_data) return OutOfMemory; dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left); get_bitmap_sample_size(interpolation, imageAttributes->wrap, bitmap, srcx, srcy, srcwidth, srcheight, &src_area); + TRACE("src_area: %d x %d\n", src_area.Width, src_area.Height); + src_data = GdipAlloc(sizeof(ARGB) * src_area.Width * src_area.Height); if (!src_data) { GdipFree(dst_data); - GdipDeleteMatrix(dst_to_src); return OutOfMemory; } src_stride = sizeof(ARGB) * src_area.Width; @@ -3205,8 +3288,7 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image if (src_data != dst_data) GdipFree(src_data); GdipFree(dst_data); - GdipDeleteMatrix(dst_to_src); - return OutOfMemory; + return stat; } apply_image_attributes(imageAttributes, src_data, @@ -3214,7 +3296,7 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image src_stride, ColorAdjustTypeBitmap); /* Transform the bits as needed to the destination. */ - GdipTransformMatrixPoints(dst_to_src, dst_to_src_points, 3); + GdipTransformMatrixPoints(&dst_to_src, dst_to_src_points, 3); x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X; x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y; @@ -3234,14 +3316,13 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left)); if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight) - *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf, imageAttributes, interpolation); + *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf, + imageAttributes, interpolation, offset_mode); else *dst_color = 0; } } - GdipDeleteMatrix(dst_to_src); - GdipFree(src_data); stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top, @@ -3293,11 +3374,19 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image convert_pixels(bitmap->width, bitmap->height, bitmap->width*4, temp_bits, dst_format, - bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries); + bitmap->stride, bitmap->bits, bitmap->format, + bitmap->image.palette); } else { - hbitmap = bitmap->hbitmap; + if (bitmap->hbitmap) + hbitmap = bitmap->hbitmap; + else + { + GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0); + temp_bitmap = 1; + } + hdc = bitmap->hdc; temp_hdc = (hdc == 0); } @@ -3329,10 +3418,22 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image DeleteObject(hbitmap); } } + else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf) + { + GpRectF rc; + + rc.X = srcx; + rc.Y = srcy; + rc.Width = srcwidth; + rc.Height = srcheight; + + return GdipEnumerateMetafileSrcRectDestPoints(graphics, (GpMetafile*)image, + points, count, &rc, srcUnit, play_metafile_proc, image, imageAttributes); + } else { - ERR("GpImage with no IPicture or HBITMAP?!\n"); - return NotImplemented; + WARN("GpImage with nothing we can draw (metafile in wrong state?)\n"); + return InvalidParameter; } return Ok; @@ -3965,7 +4066,7 @@ GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *p if(graphics->busy) return ObjectBusy; - if (!graphics->image) + if (!graphics->image && !graphics->alpha_hdc) stat = GDI32_GdipFillPath(graphics, brush, path); if (stat == NotImplemented) @@ -4219,7 +4320,7 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush, { GpStatus stat; GpRegion *temp_region; - GpMatrix *world_to_device; + GpMatrix world_to_device; GpRectF graphics_bounds; DWORD *pixel_data; HRGN hregion; @@ -4240,11 +4341,7 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush, CoordinateSpaceWorld, &world_to_device); if (stat == Ok) - { - stat = GdipTransformRegion(temp_region, world_to_device); - - GdipDeleteMatrix(world_to_device); - } + stat = GdipTransformRegion(temp_region, &world_to_device); if (stat == Ok) stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect); @@ -4307,7 +4404,7 @@ GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush, if(graphics->busy) return ObjectBusy; - if (!graphics->image) + if (!graphics->image && !graphics->alpha_hdc) stat = GDI32_GdipFillRegion(graphics, brush, region); if (stat == NotImplemented) @@ -4570,10 +4667,10 @@ GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok) { - rect->X = roundr(rectf.X); - rect->Y = roundr(rectf.Y); - rect->Width = roundr(rectf.Width); - rect->Height = roundr(rectf.Height); + rect->X = gdip_round(rectf.X); + rect->Y = gdip_round(rectf.Y); + rect->Width = gdip_round(rectf.Width); + rect->Height = gdip_round(rectf.Height); } return stat; @@ -4589,7 +4686,7 @@ GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix if(graphics->busy) return ObjectBusy; - *matrix = *graphics->worldtrans; + *matrix = graphics->worldtrans; return Ok; } @@ -4738,11 +4835,8 @@ GpStatus gdip_format_string(HDC hdc, stringdup = GdipAlloc((length + 1) * sizeof(WCHAR)); if(!stringdup) return OutOfMemory; - nwidth = roundr(rect->Width); - nheight = roundr(rect->Height); - - if (rect->Width >= INT_MAX || rect->Width < 0.5) nwidth = INT_MAX; - if (rect->Height >= INT_MAX || rect->Height < 0.5) nheight = INT_MAX; + nwidth = rect->Width; + nheight = rect->Height; if (format) hkprefix = format->hkprefix; @@ -4768,6 +4862,10 @@ GpStatus gdip_format_string(HDC hdc, if(!isprintW(string[i]) && (string[i] != '\n')) continue; + /* FIXME: tabs should be handled using tabstops from stringformat */ + if (string[i] == '\t') + continue; + if (seen_prefix && hkprefix == HotkeyPrefixShow && string[i] != '&') hotkeyprefix_offsets[hotkeyprefix_count++] = j; else if (!seen_prefix && hkprefix != HotkeyPrefixNone && string[i] == '&') @@ -4874,7 +4972,8 @@ GpStatus gdip_format_string(HDC hdc, break; /* Stop if this was a linewrap (but not if it was a linebreak). */ - if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap)) + if ((lret == fitcpy) && format && + (format->attr & (StringFormatFlagsNoWrap | StringFormatFlagsLineLimit))) break; } @@ -4886,6 +4985,7 @@ GpStatus gdip_format_string(HDC hdc, struct measure_ranges_args { GpRegion **regions; + REAL rel_width, rel_height; }; static GpStatus measure_ranges_callback(HDC hdc, @@ -4907,16 +5007,16 @@ static GpStatus measure_ranges_callback(HDC hdc, GpRectF range_rect; SIZE range_size; - range_rect.Y = bounds->Y; - range_rect.Height = bounds->Height; + range_rect.Y = bounds->Y / args->rel_height; + range_rect.Height = bounds->Height / args->rel_height; GetTextExtentExPointW(hdc, string + index, range_start - index, INT_MAX, NULL, NULL, &range_size); - range_rect.X = bounds->X + range_size.cx; + range_rect.X = (bounds->X + range_size.cx) / args->rel_width; GetTextExtentExPointW(hdc, string + index, range_end - index, INT_MAX, NULL, NULL, &range_size); - range_rect.Width = (bounds->X + range_size.cx) - range_rect.X; + range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X; stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion); if (stat != Ok) @@ -4934,10 +5034,12 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics, { GpStatus stat; int i; - LOGFONTW lfw; - HFONT oldfont; + HFONT gdifont, oldfont; struct measure_ranges_args args; HDC hdc, temp_hdc=NULL; + GpPointF pt[3]; + RectF scaled_rect; + REAL margin_x; TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string), length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions); @@ -4948,9 +5050,6 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics, if (regionCount < stringFormat->range_count) return InvalidParameter; - stat = GdipGetLogFontW((GpFont *)font, graphics, &lfw); - if (stat != Ok) return stat; - if(!graphics->hdc) { hdc = temp_hdc = CreateCompatibleDC(0); @@ -4962,7 +5061,41 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics, if (stringFormat->attr) TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr); - oldfont = SelectObject(hdc, CreateFontIndirectW(&lfw)); + 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); + args.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)); + args.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)); + + margin_x = stringFormat->generic_typographic ? 0.0 : font->emSize / 6.0; + margin_x *= units_scale(font->unit, graphics->unit, graphics->xres); + + scaled_rect.X = (layoutRect->X + margin_x) * args.rel_width; + scaled_rect.Y = layoutRect->Y * args.rel_height; + if (stringFormat->attr & StringFormatFlagsNoClip) + { + scaled_rect.Width = (REAL)(1 << 23); + scaled_rect.Height = (REAL)(1 << 23); + } + else + { + scaled_rect.Width = layoutRect->Width * args.rel_width; + scaled_rect.Height = layoutRect->Height * args.rel_height; + } + if (scaled_rect.Width >= 0.5) + { + scaled_rect.Width -= margin_x * 2.0 * args.rel_width; + if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */ + } + + get_font_hfont(graphics, font, stringFormat, &gdifont, NULL); + oldfont = SelectObject(hdc, gdifont); for (i=0; irange_count; i++) { @@ -4973,10 +5106,11 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics, args.regions = regions; - stat = gdip_format_string(hdc, string, length, font, layoutRect, stringFormat, + stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat, measure_ranges_callback, &args); - DeleteObject(SelectObject(hdc, oldfont)); + SelectObject(hdc, oldfont); + DeleteObject(gdifont); if (temp_hdc) DeleteDC(temp_hdc); @@ -5001,7 +5135,7 @@ static GpStatus measure_string_callback(HDC hdc, REAL new_width, new_height; new_width = bounds->Width / args->rel_width; - new_height = (bounds->Height + bounds->Y - args->bounds->Y) / args->rel_height; + new_height = (bounds->Height + bounds->Y) / args->rel_height - args->bounds->Y; if (new_width > args->bounds->Width) args->bounds->Width = new_width; @@ -5031,6 +5165,9 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, struct measure_string_args args; HDC temp_hdc=NULL, hdc; GpPointF pt[3]; + RectF scaled_rect; + REAL margin_x; + INT lines, glyphs, format_flags = format ? format->attr : 0; TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics, debugstr_wn(string, length), length, font, debugstr_rectf(rect), format, @@ -5065,7 +5202,29 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, args.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)); - get_font_hfont(graphics, font, &gdifont); + margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0; + margin_x *= units_scale(font->unit, graphics->unit, graphics->xres); + + scaled_rect.X = (rect->X + margin_x) * args.rel_width; + scaled_rect.Y = rect->Y * args.rel_height; + scaled_rect.Width = rect->Width * args.rel_width; + scaled_rect.Height = rect->Height * args.rel_height; + + if ((format_flags & StringFormatFlagsNoClip) || + scaled_rect.Width >= 1 << 23 || scaled_rect.Width < 0.5) scaled_rect.Width = 1 << 23; + if ((format_flags & StringFormatFlagsNoClip) || + scaled_rect.Height >= 1 << 23 || scaled_rect.Height < 0.5) scaled_rect.Height = 1 << 23; + + if (scaled_rect.Width >= 0.5) + { + scaled_rect.Width -= margin_x * 2.0 * args.rel_width; + if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */ + } + + if (scaled_rect.Width >= 1 << 23 || scaled_rect.Width < 0.5) scaled_rect.Width = 1 << 23; + if (scaled_rect.Height >= 1 << 23 || scaled_rect.Height < 0.5) scaled_rect.Height = 1 << 23; + + get_font_hfont(graphics, font, format, &gdifont, NULL); oldfont = SelectObject(hdc, gdifont); bounds->X = rect->X; @@ -5074,12 +5233,19 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, bounds->Height = 0.0; args.bounds = bounds; - args.codepointsfitted = codepointsfitted; - args.linesfilled = linesfilled; + args.codepointsfitted = &glyphs; + args.linesfilled = &lines; + lines = glyphs = 0; - gdip_format_string(hdc, string, length, font, rect, format, + gdip_format_string(hdc, string, length, font, &scaled_rect, format, measure_string_callback, &args); + if (linesfilled) *linesfilled = lines; + if (codepointsfitted) *codepointsfitted = glyphs; + + if (lines) + bounds->Width += margin_x * 2.0; + SelectObject(hdc, oldfont); DeleteObject(gdifont); @@ -5108,7 +5274,7 @@ static GpStatus draw_string_callback(HDC hdc, position.X = args->x + bounds->X / args->rel_width; position.Y = args->y + bounds->Y / args->rel_height + args->ascent; - stat = GdipDrawDriverString(args->graphics, &string[index], length, font, + stat = draw_driver_string(args->graphics, &string[index], length, font, format, args->brush, &position, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); @@ -5150,8 +5316,8 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string HFONT gdifont; GpPointF pt[3], rectcpy[4]; POINT corners[4]; - REAL rel_width, rel_height; - INT save_state; + REAL rel_width, rel_height, margin_x; + INT save_state, format_flags = 0; REAL offsety = 0.0; struct draw_string_args args; RectF scaled_rect; @@ -5176,17 +5342,23 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string if(format){ TRACE("may be ignoring some format flags: attr %x\n", format->attr); + format_flags = 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); + RectF bounds, in_rect = *rect; + in_rect.Height = 0.0; /* avoid height clipping */ + GdipMeasureString(graphics, string, length, font, &in_rect, format, &bounds, 0, 0); + + TRACE("bounds %s\n", debugstr_rectf(&bounds)); if(format->vertalign == StringAlignmentCenter) offsety = (rect->Height - bounds.Height) / 2; else if(format->vertalign == StringAlignmentFar) offsety = (rect->Height - bounds.Height); } + TRACE("vertical align %d, offsety %f\n", format->vertalign, offsety); } save_state = SaveDC(hdc); @@ -5204,24 +5376,42 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string (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[1].Y = rectcpy[0].Y = rect->Y; rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width; - rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height; + rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height; transform_and_round_points(graphics, corners, rectcpy, 4); - scaled_rect.X = 0.0; + margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0; + margin_x *= units_scale(font->unit, graphics->unit, graphics->xres); + + scaled_rect.X = margin_x * rel_width; 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) + if ((format_flags & StringFormatFlagsNoClip) || + scaled_rect.Width >= 1 << 23 || scaled_rect.Width < 0.5) scaled_rect.Width = 1 << 23; + if ((format_flags & StringFormatFlagsNoClip) || + scaled_rect.Height >= 1 << 23 || scaled_rect.Height < 0.5) scaled_rect.Height = 1 << 23; + + if (scaled_rect.Width >= 0.5) + { + scaled_rect.Width -= margin_x * 2.0 * rel_width; + if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */ + } + + if (scaled_rect.Width >= 1 << 23 || scaled_rect.Width < 0.5) scaled_rect.Width = 1 << 23; + if (scaled_rect.Height >= 1 << 23 || scaled_rect.Height < 0.5) scaled_rect.Height = 1 << 23; + + if (!(format_flags & StringFormatFlagsNoClip) && + scaled_rect.Width != 1 << 23 && scaled_rect.Height != 1 << 23) { /* FIXME: If only the width or only the height is 0, we should probably still clip */ rgn = CreatePolygonRgn(corners, 4, ALTERNATE); SelectClipRgn(hdc, rgn); } - get_font_hfont(graphics, font, &gdifont); + get_font_hfont(graphics, font, format, &gdifont, NULL); SelectObject(hdc, gdifont); args.graphics = graphics; @@ -5272,14 +5462,7 @@ GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics) if(graphics->busy) return ObjectBusy; - graphics->worldtrans->matrix[0] = 1.0; - graphics->worldtrans->matrix[1] = 0.0; - graphics->worldtrans->matrix[2] = 0.0; - graphics->worldtrans->matrix[3] = 1.0; - graphics->worldtrans->matrix[4] = 0.0; - graphics->worldtrans->matrix[5] = 0.0; - - return Ok; + return GdipSetMatrixElements(&graphics->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); } GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state) @@ -5298,7 +5481,7 @@ GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle, if(graphics->busy) return ObjectBusy; - return GdipRotateMatrix(graphics->worldtrans, angle, order); + return GdipRotateMatrix(&graphics->worldtrans, angle, order); } GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state) @@ -5393,7 +5576,7 @@ GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx, if(graphics->busy) return ObjectBusy; - return GdipScaleMatrix(graphics->worldtrans, sx, sy, order); + return GdipScaleMatrix(&graphics->worldtrans, sx, sy, order); } GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics, @@ -5594,8 +5777,13 @@ GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix if(graphics->busy) return ObjectBusy; - GdipDeleteMatrix(graphics->worldtrans); - return GdipCloneMatrix(matrix, &graphics->worldtrans); + TRACE("%f,%f,%f,%f,%f,%f\n", + matrix->matrix[0], matrix->matrix[1], matrix->matrix[2], + matrix->matrix[3], matrix->matrix[4], matrix->matrix[5]); + + graphics->worldtrans = *matrix; + + return Ok; } GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx, @@ -5609,7 +5797,7 @@ GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx, if(graphics->busy) return ObjectBusy; - return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order); + return GdipTranslateMatrix(&graphics->worldtrans, dx, dy, order); } /***************************************************************************** @@ -5779,11 +5967,7 @@ GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi) if(graphics->busy) return ObjectBusy; - if (graphics->image) - *dpi = graphics->image->xres; - else - *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX); - + *dpi = graphics->xres; return Ok; } @@ -5797,11 +5981,7 @@ GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi) if(graphics->busy) return ObjectBusy; - if (graphics->image) - *dpi = graphics->image->yres; - else - *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY); - + *dpi = graphics->yres; return Ok; } @@ -5819,11 +5999,11 @@ GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST G if(graphics->busy) return ObjectBusy; - m = *(graphics->worldtrans); + m = graphics->worldtrans; ret = GdipMultiplyMatrix(&m, matrix, order); if(ret == Ok) - *(graphics->worldtrans) = m; + graphics->worldtrans = m; return ret; } @@ -5847,7 +6027,7 @@ GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc) { stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc); } - else if (!graphics->hdc || + else if (!graphics->hdc || graphics->alpha_hdc || (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha)) { /* Create a fake HDC and fill it with a constant color. */ @@ -5983,28 +6163,34 @@ GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region) } static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space, - GpCoordinateSpace src_space, GpMatrix **matrix) + GpCoordinateSpace src_space, GpMatrix *matrix) { - GpStatus stat = GdipCreateMatrix(matrix); - REAL unitscale; + GpStatus stat = Ok; + REAL scale_x, scale_y; - if (dst_space != src_space && stat == Ok) + GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + + if (dst_space != src_space) { - unitscale = convert_unit(graphics_res(graphics), graphics->unit); + scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres); + scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres); if(graphics->unit != UnitDisplay) - unitscale *= graphics->scale; + { + scale_x *= graphics->scale; + scale_y *= graphics->scale; + } /* transform from src_space to CoordinateSpacePage */ switch (src_space) { case CoordinateSpaceWorld: - GdipMultiplyMatrix(*matrix, graphics->worldtrans, MatrixOrderAppend); + GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend); break; case CoordinateSpacePage: break; case CoordinateSpaceDevice: - GdipScaleMatrix(*matrix, 1.0/unitscale, 1.0/unitscale, MatrixOrderAppend); + GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend); break; } @@ -6013,21 +6199,16 @@ static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace d { case CoordinateSpaceWorld: { - GpMatrix *inverted_transform; - stat = GdipCloneMatrix(graphics->worldtrans, &inverted_transform); + GpMatrix inverted_transform = graphics->worldtrans; + stat = GdipInvertMatrix(&inverted_transform); if (stat == Ok) - { - stat = GdipInvertMatrix(inverted_transform); - if (stat == Ok) - GdipMultiplyMatrix(*matrix, inverted_transform, MatrixOrderAppend); - GdipDeleteMatrix(inverted_transform); - } + GdipMultiplyMatrix(matrix, &inverted_transform, MatrixOrderAppend); break; } case CoordinateSpacePage: break; case CoordinateSpaceDevice: - GdipScaleMatrix(*matrix, unitscale, unitscale, MatrixOrderAppend); + GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend); break; } } @@ -6037,7 +6218,7 @@ static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace d GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space, GpCoordinateSpace src_space, GpPointF *points, INT count) { - GpMatrix *matrix; + GpMatrix matrix; GpStatus stat; if(!graphics || !points || count <= 0) @@ -6051,15 +6232,9 @@ GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace if (src_space == dst_space) return Ok; stat = get_graphics_transform(graphics, dst_space, src_space, &matrix); + if (stat != Ok) return stat; - if (stat == Ok) - { - stat = GdipTransformMatrixPoints(matrix, points, count); - - GdipDeleteMatrix(matrix); - } - - return stat; + return GdipTransformMatrixPoints(&matrix, points, count); } GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space, @@ -6087,8 +6262,8 @@ GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace if(ret == Ok) for(i = 0; i < count; i++){ - points[i].X = roundr(pointsF[i].X); - points[i].Y = roundr(pointsF[i].Y); + points[i].X = gdip_round(pointsF[i].X); + points[i].Y = gdip_round(pointsF[i].Y); } GdipFree(pointsF); @@ -6177,10 +6352,7 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT if (flags & unsupported_flags) FIXME("Ignoring flags %x\n", flags & unsupported_flags); - if (matrix) - FIXME("Ignoring matrix\n"); - - get_font_hfont(graphics, font, &hfont); + get_font_hfont(graphics, font, NULL, &hfont, matrix); hdc = CreateCompatibleDC(0); SelectObject(hdc, hfont); @@ -6193,6 +6365,11 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT pt[1].Y = 0.0; pt[2].X = 0.0; pt[2].Y = 1.0; + if (matrix) + { + GpMatrix xform = *matrix; + GdipTransformMatrixPoints(&xform, pt, 3); + } GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3); 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)); @@ -6232,7 +6409,7 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT } GetCharABCWidthsW(hdc, glyph_indices[i], glyph_indices[i], &abc); - char_width = abc.abcA + abc.abcB + abc.abcB; + char_width = abc.abcA + abc.abcB + abc.abcC; if (min_y > y - ascent) min_y = y - ascent; if (max_y < y + descent) max_y = y + descent; @@ -6256,9 +6433,9 @@ GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT } static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length, - GDIPCONST GpFont *font, GDIPCONST GpBrush *brush, - GDIPCONST PointF *positions, INT flags, - GDIPCONST GpMatrix *matrix ) + GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, + GDIPCONST GpBrush *brush, GDIPCONST PointF *positions, + INT flags, GDIPCONST GpMatrix *matrix) { static const INT unsupported_flags = ~(DriverStringOptionsRealizedAdvance|DriverStringOptionsCmapLookup); INT save_state; @@ -6269,9 +6446,6 @@ static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT1 if (flags & unsupported_flags) FIXME("Ignoring flags %x\n", flags & unsupported_flags); - if (matrix) - FIXME("Ignoring matrix\n"); - if (!(flags & DriverStringOptionsCmapLookup)) eto_flags |= ETO_GLYPH_INDEX; @@ -6282,12 +6456,12 @@ static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT1 pt = positions[0]; GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &pt, 1); - get_font_hfont(graphics, font, &hfont); + get_font_hfont(graphics, font, format, &hfont, matrix); SelectObject(graphics->hdc, hfont); SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT); - ExtTextOutW(graphics->hdc, roundr(pt.X), roundr(pt.Y), eto_flags, NULL, text, length, NULL); + ExtTextOutW(graphics->hdc, gdip_round(pt.X), gdip_round(pt.Y), eto_flags, NULL, text, length, NULL); RestoreDC(graphics->hdc, save_state); @@ -6297,9 +6471,9 @@ static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT1 } static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length, - GDIPCONST GpFont *font, GDIPCONST GpBrush *brush, - GDIPCONST PointF *positions, INT flags, - GDIPCONST GpMatrix *matrix ) + GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, + GDIPCONST GpBrush *brush, GDIPCONST PointF *positions, + INT flags, GDIPCONST GpMatrix *matrix) { static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance); GpStatus stat; @@ -6328,9 +6502,6 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI if (flags & unsupported_flags) FIXME("Ignoring flags %x\n", flags & unsupported_flags); - if (matrix) - FIXME("Ignoring matrix\n"); - pti = GdipAlloc(sizeof(POINT) * length); if (!pti) return OutOfMemory; @@ -6357,7 +6528,7 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI GdipFree(real_positions); } - get_font_hfont(graphics, font, &hfont); + get_font_hfont(graphics, font, format, &hfont, matrix); hdc = CreateCompatibleDC(0); SelectObject(hdc, hfont); @@ -6490,6 +6661,28 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI return stat; } +static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length, + GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, + GDIPCONST GpBrush *brush, GDIPCONST PointF *positions, + INT flags, GDIPCONST GpMatrix *matrix) +{ + GpStatus stat = NotImplemented; + + if (length == -1) + length = strlenW(text); + + if (graphics->hdc && !graphics->alpha_hdc && + ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) && + brush->bt == BrushTypeSolidColor && + (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000) + stat = GDI32_GdipDrawDriverString(graphics, text, length, font, format, + brush, positions, flags, matrix); + if (stat == NotImplemented) + stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, format, + brush, positions, flags, matrix); + return stat; +} + /***************************************************************************** * GdipDrawDriverString [GDIPLUS.@] */ @@ -6498,28 +6691,13 @@ GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix ) { - GpStatus stat=NotImplemented; - TRACE("(%p %s %p %p %p %d %p)\n", graphics, debugstr_wn(text, length), font, brush, positions, flags, matrix); if (!graphics || !text || !font || !brush || !positions) return InvalidParameter; - if (length == -1) - length = strlenW(text); - - if (graphics->hdc && - ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) && - brush->bt == BrushTypeSolidColor && - (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000) - stat = GDI32_GdipDrawDriverString(graphics, text, length, font, brush, - positions, flags, matrix); - - if (stat == NotImplemented) - stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, brush, - positions, flags, matrix); - - return stat; + return draw_driver_string(graphics, text, length, font, NULL, + brush, positions, flags, matrix); } GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect, diff --git a/reactos/dll/win32/gdiplus/graphicspath.c b/reactos/dll/win32/gdiplus/graphicspath.c index 5da53b2f0cc..1fbebba6d4c 100644 --- a/reactos/dll/win32/gdiplus/graphicspath.c +++ b/reactos/dll/win32/gdiplus/graphicspath.c @@ -155,7 +155,7 @@ static BOOL flatten_bezier(path_list_node_t *start, REAL x2, REAL y2, REAL x3, R if(!(node = add_path_list_node(start, mp[2].X, mp[2].Y, PathPointTypeLine))) return FALSE; - /* do the same with halfs */ + /* do the same with halves */ flatten_bezier(start, mp[0].X, mp[0].Y, mp[1].X, mp[1].Y, node, flatness); flatten_bezier(node, mp[3].X, mp[3].Y, mp[4].X, mp[4].Y, end, flatness); @@ -840,7 +840,9 @@ static float fromfixedpoint(const FIXED v) struct format_string_args { GpPath *path; - UINT maxY; + float maxY; + float scale; + float ascent; }; static GpStatus format_string_callback(HDC dc, @@ -853,20 +855,22 @@ static GpStatus format_string_callback(HDC dc, struct format_string_args *args = priv; GpPath *path = args->path; GpStatus status = Ok; - float x = bounds->X; - float y = bounds->Y; + float x = rect->X + (bounds->X - rect->X) * args->scale; + float y = rect->Y + (bounds->Y - rect->Y) * args->scale; int i; if (underlined_index_count) FIXME("hotkey underlines not drawn yet\n"); + if (y + bounds->Height * args->scale > args->maxY) + args->maxY = y + bounds->Height * args->scale; + for (i = index; i < length; ++i) { GLYPHMETRICS gm; TTPOLYGONHEADER *ph = NULL; char *start; DWORD len, ofs = 0; - UINT bb_end; len = GetGlyphOutlineW(dc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity); if (len == GDI_ERROR) { @@ -881,9 +885,6 @@ static GpStatus format_string_callback(HDC dc, break; } GetGlyphOutlineW(dc, string[i], GGO_BEZIER, &gm, len, start, &identity); - bb_end = gm.gmBlackBoxY + gm.gmptGlyphOrigin.y; - if (bb_end + y > args->maxY) - args->maxY = bb_end + y; ofs = 0; while (ofs < len) @@ -891,8 +892,8 @@ static GpStatus format_string_callback(HDC dc, DWORD ofs_start = ofs; ph = (TTPOLYGONHEADER*)&start[ofs]; path->pathdata.Types[path->pathdata.Count] = PathPointTypeStart; - path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(ph->pfxStart.x); - path->pathdata.Points[path->pathdata.Count++].Y = y + bb_end - fromfixedpoint(ph->pfxStart.y); + path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(ph->pfxStart.x) * args->scale; + path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(ph->pfxStart.y) * args->scale; TRACE("Starting at count %i with pos %f, %f)\n", path->pathdata.Count, x, y); ofs += sizeof(*ph); while (ofs - ofs_start < ph->cb) @@ -907,16 +908,16 @@ static GpStatus format_string_callback(HDC dc, for (j = 0; j < curve->cpfx; ++j) { path->pathdata.Types[path->pathdata.Count] = PathPointTypeLine; - path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x); - path->pathdata.Points[path->pathdata.Count++].Y = y + bb_end - fromfixedpoint(curve->apfx[j].y); + path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x) * args->scale; + path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(curve->apfx[j].y) * args->scale; } break; case TT_PRIM_CSPLINE: for (j = 0; j < curve->cpfx; ++j) { path->pathdata.Types[path->pathdata.Count] = PathPointTypeBezier; - path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x); - path->pathdata.Points[path->pathdata.Count++].Y = y + bb_end - fromfixedpoint(curve->apfx[j].y); + path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x) * args->scale; + path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(curve->apfx[j].y) * args->scale; } break; default: @@ -927,8 +928,8 @@ static GpStatus format_string_callback(HDC dc, path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath; } path->newfigure = TRUE; - x += gm.gmCellIncX; - y += gm.gmCellIncY; + x += gm.gmCellIncX * args->scale; + y += gm.gmCellIncY * args->scale; GdipFree(ph); if (status != Ok) @@ -945,39 +946,70 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT LOGFONTW lfw; HANDLE hfont; HDC dc; + GpGraphics *graphics; GpPath *backup; struct format_string_args args; int i; + UINT16 native_height; + RectF scaled_layout_rect; + TEXTMETRICW textmetric; - FIXME("(%p, %s, %d, %p, %d, %f, %p, %p): stub\n", path, debugstr_w(string), length, family, style, emSize, layoutRect, format); + TRACE("(%p, %s, %d, %p, %d, %f, %p, %p)\n", path, debugstr_w(string), length, family, style, emSize, layoutRect, format); if (!path || !string || !family || !emSize || !layoutRect || !format) return InvalidParameter; - status = GdipCreateFont(family, emSize, style, UnitPixel, &font); + status = GdipGetEmHeight(family, style, &native_height); if (status != Ok) return status; - status = GdipGetLogFontW((GpFont *)font, NULL, &lfw); - if (status != Ok) return status; + scaled_layout_rect.X = layoutRect->X; + scaled_layout_rect.Y = layoutRect->Y; + scaled_layout_rect.Width = layoutRect->Width * native_height / emSize; + scaled_layout_rect.Height = layoutRect->Height * native_height / emSize; + + if ((status = GdipClonePath(path, &backup)) != Ok) + return status; + + dc = CreateCompatibleDC(0); + status = GdipCreateFromHDC(dc, &graphics); + if (status != Ok) + { + DeleteDC(dc); + GdipDeletePath(backup); + return status; + } + + status = GdipCreateFont(family, native_height, style, UnitPixel, &font); + if (status != Ok) + { + GdipDeleteGraphics(graphics); + DeleteDC(dc); + GdipDeletePath(backup); + return status; + } + + get_log_fontW(font, graphics, &lfw); + GdipDeleteFont(font); + GdipDeleteGraphics(graphics); + hfont = CreateFontIndirectW(&lfw); if (!hfont) { WARN("Failed to create font\n"); + DeleteDC(dc); + GdipDeletePath(backup); return GenericError; } - if ((status = GdipClonePath(path, &backup)) != Ok) - { - DeleteObject(hfont); - return status; - } - - dc = CreateCompatibleDC(0); SelectObject(dc, hfont); + GetTextMetricsW(dc, &textmetric); + args.path = path; args.maxY = 0; - status = gdip_format_string(dc, string, length, NULL, layoutRect, format, format_string_callback, &args); + args.scale = emSize / native_height; + args.ascent = textmetric.tmAscent * args.scale; + status = gdip_format_string(dc, string, length, NULL, &scaled_layout_rect, format, format_string_callback, &args); DeleteDC(dc); DeleteObject(hfont); @@ -992,12 +1024,12 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT } if (format && format->vertalign == StringAlignmentCenter && layoutRect->Y + args.maxY < layoutRect->Height) { - float inc = layoutRect->Height - args.maxY - layoutRect->Y; + float inc = layoutRect->Height + layoutRect->Y - args.maxY; inc /= 2; for (i = backup->pathdata.Count; i < path->pathdata.Count; ++i) path->pathdata.Points[i].Y += inc; } else if (format && format->vertalign == StringAlignmentFar) { - float inc = layoutRect->Height - args.maxY - layoutRect->Y; + float inc = layoutRect->Height + layoutRect->Y - args.maxY; for (i = backup->pathdata.Count; i < path->pathdata.Count; ++i) path->pathdata.Points[i].Y += inc; } @@ -1035,9 +1067,9 @@ GpStatus WINGDIPAPI GdipClonePath(GpPath* path, GpPath **clone) (*clone)->pathdata.Points = GdipAlloc(path->datalen * sizeof(PointF)); (*clone)->pathdata.Types = GdipAlloc(path->datalen); if(!(*clone)->pathdata.Points || !(*clone)->pathdata.Types){ - GdipFree(*clone); GdipFree((*clone)->pathdata.Points); GdipFree((*clone)->pathdata.Types); + GdipFree(*clone); return OutOfMemory; } @@ -1173,20 +1205,22 @@ GpStatus WINGDIPAPI GdipFlattenPath(GpPath *path, GpMatrix* matrix, REAL flatnes GpPointF pt; INT i = 1; INT startidx = 0; + GpStatus stat; TRACE("(%p, %p, %.2f)\n", path, matrix, flatness); if(!path) return InvalidParameter; - if(matrix){ - WARN("transformation not supported yet!\n"); - return NotImplemented; - } - if(path->pathdata.Count == 0) return Ok; + if(matrix){ + stat = GdipTransformPath(path, matrix); + if (stat != Ok) + return stat; + } + pt = path->pathdata.Points[0]; if(!init_path_list(&list, pt.X, pt.Y)) return OutOfMemory; @@ -1339,8 +1373,8 @@ GpStatus WINGDIPAPI GdipGetPathPointsI(GpPath *path, GpPoint* points, INT count) ret = GdipGetPathPoints(path,ptf,count); if(ret == Ok) for(i = 0;i < count;i++){ - points[i].X = roundr(ptf[i].X); - points[i].Y = roundr(ptf[i].Y); + points[i].X = gdip_round(ptf[i].X); + points[i].Y = gdip_round(ptf[i].Y); }; GdipFree(ptf); @@ -1460,10 +1494,10 @@ GpStatus WINGDIPAPI GdipGetPathWorldBoundsI(GpPath* path, GpRect* bounds, ret = GdipGetPathWorldBounds(path,&boundsF,matrix,pen); if(ret == Ok){ - bounds->X = roundr(boundsF.X); - bounds->Y = roundr(boundsF.Y); - bounds->Width = roundr(boundsF.Width); - bounds->Height = roundr(boundsF.Height); + bounds->X = gdip_round(boundsF.X); + bounds->Y = gdip_round(boundsF.Y); + bounds->Width = gdip_round(boundsF.Width); + bounds->Height = gdip_round(boundsF.Height); } return ret; @@ -1589,7 +1623,7 @@ GpStatus WINGDIPAPI GdipIsVisiblePathPoint(GpPath* path, REAL x, REAL y, GpGraph return status; } - *result = PtInRegion(hrgn, roundr(x), roundr(y)); + *result = PtInRegion(hrgn, gdip_round(x), gdip_round(y)); DeleteObject(hrgn); GdipDeleteRegion(region); @@ -1688,6 +1722,35 @@ static void widen_joint(const GpPointF *p1, const GpPointF *p2, const GpPointF * { switch (pen->join) { + case LineJoinMiter: + case LineJoinMiterClipped: + if ((p2->X - p1->X) * (p3->Y - p1->Y) > (p2->Y - p1->Y) * (p3->X - p1->X)) + { + float distance = pen->width/2.0; + float length_0 = sqrtf((p2->X-p1->X)*(p2->X-p1->X)+(p2->Y-p1->Y)*(p2->Y-p1->Y)); + float length_1 = sqrtf((p3->X-p2->X)*(p3->X-p2->X)+(p3->Y-p2->Y)*(p3->Y-p2->Y)); + float dx0 = distance * (p2->X - p1->X) / length_0; + float dy0 = distance * (p2->Y - p1->Y) / length_0; + float dx1 = distance * (p3->X - p2->X) / length_1; + float dy1 = distance * (p3->Y - p2->Y) / length_1; + float det = (dy0*dx1 - dx0*dy1); + float dx = (dx0*dx1*(dx0-dx1) + dy0*dy0*dx1 - dy1*dy1*dx0)/det; + float dy = (dy0*dy1*(dy0-dy1) + dx0*dx0*dy1 - dx1*dx1*dy0)/det; + if (dx*dx + dy*dy < pen->miterlimit*pen->miterlimit * distance*distance) + { + *last_point = add_path_list_node(*last_point, p2->X + dx, + p2->Y + dy, PathPointTypeLine); + break; + } + else if (pen->join == LineJoinMiter) + { + static int once; + if (!once++) + FIXME("should add a clipped corner\n"); + } + /* else fall-through */ + } + /* else fall-through */ default: case LineJoinBevel: add_bevel_point(p2, p1, pen, 1, last_point); @@ -1709,6 +1772,74 @@ static void widen_cap(const GpPointF *endpoint, const GpPointF *nextpoint, if (add_last_point) add_bevel_point(endpoint, nextpoint, pen, 0, last_point); break; + case LineCapSquare: + { + REAL segment_dy = nextpoint->Y-endpoint->Y; + REAL segment_dx = nextpoint->X-endpoint->X; + REAL segment_length = sqrtf(segment_dy*segment_dy + segment_dx*segment_dx); + REAL distance = pen->width/2.0; + REAL bevel_dx, bevel_dy; + REAL extend_dx, extend_dy; + + extend_dx = -distance * segment_dx / segment_length; + extend_dy = -distance * segment_dy / segment_length; + + bevel_dx = -distance * segment_dy / segment_length; + bevel_dy = distance * segment_dx / segment_length; + + if (add_first_points) + *last_point = add_path_list_node(*last_point, endpoint->X + extend_dx + bevel_dx, + endpoint->Y + extend_dy + bevel_dy, PathPointTypeLine); + + if (add_last_point) + *last_point = add_path_list_node(*last_point, endpoint->X + extend_dx - bevel_dx, + endpoint->Y + extend_dy - bevel_dy, PathPointTypeLine); + + break; + } + case LineCapRound: + { + REAL segment_dy = nextpoint->Y-endpoint->Y; + REAL segment_dx = nextpoint->X-endpoint->X; + REAL segment_length = sqrtf(segment_dy*segment_dy + segment_dx*segment_dx); + REAL distance = pen->width/2.0; + REAL dx, dy, dx2, dy2; + const REAL control_point_distance = 0.5522847498307935; /* 4/3 * (sqrt(2) - 1) */ + + if (add_first_points) + { + dx = -distance * segment_dx / segment_length; + dy = -distance * segment_dy / segment_length; + + dx2 = dx * control_point_distance; + dy2 = dy * control_point_distance; + + /* first 90-degree arc */ + *last_point = add_path_list_node(*last_point, endpoint->X + dy, + endpoint->Y - dx, PathPointTypeLine); + + *last_point = add_path_list_node(*last_point, endpoint->X + dy + dx2, + endpoint->Y - dx + dy2, PathPointTypeBezier); + + *last_point = add_path_list_node(*last_point, endpoint->X + dx + dy2, + endpoint->Y + dy - dx2, PathPointTypeBezier); + + /* midpoint */ + *last_point = add_path_list_node(*last_point, endpoint->X + dx, + endpoint->Y + dy, PathPointTypeBezier); + + /* second 90-degree arc */ + *last_point = add_path_list_node(*last_point, endpoint->X + dx - dy2, + endpoint->Y + dy + dx2, PathPointTypeBezier); + + *last_point = add_path_list_node(*last_point, endpoint->X - dy + dx2, + endpoint->Y + dx + dy2, PathPointTypeBezier); + + *last_point = add_path_list_node(*last_point, endpoint->X - dy, + endpoint->Y + dx, PathPointTypeBezier); + } + break; + } } } @@ -1716,15 +1847,16 @@ static void widen_open_figure(GpPath *path, GpPen *pen, int start, int end, path_list_node_t **last_point) { int i; + path_list_node_t *prev_point; if (end <= start) return; + prev_point = *last_point; + widen_cap(&path->pathdata.Points[start], &path->pathdata.Points[start+1], pen, pen->startcap, pen->customstart, FALSE, TRUE, last_point); - (*last_point)->type = PathPointTypeStart; - for (i=start+1; ipathdata.Points[i-1], &path->pathdata.Points[i], &path->pathdata.Points[i+1], pen, last_point); @@ -1739,6 +1871,7 @@ static void widen_open_figure(GpPath *path, GpPen *pen, int start, int end, widen_cap(&path->pathdata.Points[start], &path->pathdata.Points[start+1], pen, pen->startcap, pen->customstart, TRUE, FALSE, last_point); + prev_point->next->type = PathPointTypeStart; (*last_point)->type |= PathPointTypeCloseSubpath; } @@ -1813,16 +1946,16 @@ GpStatus WINGDIPAPI GdipWidenPath(GpPath *path, GpPen *pen, GpMatrix *matrix, { last_point = points; - if (pen->endcap != LineCapFlat) + if (pen->endcap > LineCapRound) FIXME("unimplemented end cap %x\n", pen->endcap); - if (pen->startcap != LineCapFlat) + if (pen->startcap > LineCapRound) FIXME("unimplemented start cap %x\n", pen->startcap); if (pen->dashcap != DashCapFlat) FIXME("unimplemented dash cap %d\n", pen->dashcap); - if (pen->join != LineJoinBevel) + if (pen->join == LineJoinRound) FIXME("unimplemented line join %d\n", pen->join); if (pen->dash != DashStyleSolid) diff --git a/reactos/dll/win32/gdiplus/image.c b/reactos/dll/win32/gdiplus/image.c index ba8b9797eb0..9d886295b8b 100644 --- a/reactos/dll/win32/gdiplus/image.c +++ b/reactos/dll/win32/gdiplus/image.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 Google (Evan Stade) + * Copyright (C) 2012 Dmitry Timoshkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,6 +18,7 @@ */ #include +#include #define NONAMELESSUNION @@ -40,6 +42,73 @@ WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); #define PIXELFORMATBPP(x) ((x) ? ((x) >> 8) & 255 : 24) +static const struct +{ + const WICPixelFormatGUID *wic_format; + PixelFormat gdip_format; + /* predefined palette type to use for pixel format conversions */ + WICBitmapPaletteType palette_type; +} pixel_formats[] = +{ + { &GUID_WICPixelFormatBlackWhite, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW }, + { &GUID_WICPixelFormat1bppIndexed, PixelFormat1bppIndexed, WICBitmapPaletteTypeFixedBW }, + { &GUID_WICPixelFormat8bppGray, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedGray256 }, + { &GUID_WICPixelFormat8bppIndexed, PixelFormat8bppIndexed, WICBitmapPaletteTypeFixedHalftone256 }, + { &GUID_WICPixelFormat16bppBGR555, PixelFormat16bppRGB555, WICBitmapPaletteTypeFixedHalftone256 }, + { &GUID_WICPixelFormat24bppBGR, PixelFormat24bppRGB, WICBitmapPaletteTypeFixedHalftone256 }, + { &GUID_WICPixelFormat32bppBGR, PixelFormat32bppRGB, WICBitmapPaletteTypeFixedHalftone256 }, + { &GUID_WICPixelFormat32bppBGRA, PixelFormat32bppARGB, WICBitmapPaletteTypeFixedHalftone256 }, + { &GUID_WICPixelFormat32bppPBGRA, PixelFormat32bppPARGB, WICBitmapPaletteTypeFixedHalftone256 }, + { NULL } +}; + +static ColorPalette *get_palette(IWICBitmapFrameDecode *frame, WICBitmapPaletteType palette_type) +{ + HRESULT hr; + IWICImagingFactory *factory; + IWICPalette *wic_palette; + ColorPalette *palette = NULL; + + hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, + &IID_IWICImagingFactory, (void **)&factory); + if (hr != S_OK) return NULL; + + hr = IWICImagingFactory_CreatePalette(factory, &wic_palette); + if (hr == S_OK) + { + hr = WINCODEC_ERR_PALETTEUNAVAILABLE; + if (frame) + hr = IWICBitmapFrameDecode_CopyPalette(frame, wic_palette); + if (hr != S_OK) + { + TRACE("using predefined palette %#x\n", palette_type); + hr = IWICPalette_InitializePredefined(wic_palette, palette_type, FALSE); + } + if (hr == S_OK) + { + UINT count; + BOOL mono, gray; + + IWICPalette_IsBlackWhite(wic_palette, &mono); + IWICPalette_IsGrayscale(wic_palette, &gray); + + IWICPalette_GetColorCount(wic_palette, &count); + palette = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(UINT) + count * sizeof(ARGB)); + IWICPalette_GetColors(wic_palette, count, palette->Entries, &palette->Count); + + if (mono) + palette->Flags = 0; + else if (gray) + palette->Flags = PaletteFlagsGrayScale; + else + palette->Flags = PaletteFlagsHalftone; + } + IWICPalette_Release(wic_palette); + } + IWICImagingFactory_Release(factory); + return palette; +} + static INT ipicture_pixel_height(IPicture *pic) { HDC hdcref; @@ -289,18 +358,21 @@ GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y, } if (bitmap->format & PixelFormatIndexed) - *color = bitmap->image.palette_entries[index]; + *color = bitmap->image.palette->Entries[index]; else *color = a<<24|r<<16|g<<8|b; return Ok; } -static inline UINT get_palette_index(BYTE r, BYTE g, BYTE b, BYTE a, GpBitmap* bitmap) { +static inline UINT get_palette_index(BYTE r, BYTE g, BYTE b, BYTE a, ColorPalette *palette) +{ BYTE index = 0; int best_distance = 0x7fff; int distance; int i; + + if (!palette) return 0; /* This algorithm scans entire palette, computes difference from desired color (all color components have equal weight) and returns the index of color with least difference. @@ -310,8 +382,8 @@ static inline UINT get_palette_index(BYTE r, BYTE g, BYTE b, BYTE a, GpBitmap* b tables and thus may actually be slower if this method is called only few times per every image. */ - for(i=0;iimage.palette_size;i++) { - ARGB color=bitmap->image.palette_entries[i]; + for(i=0;iCount;i++) { + ARGB color=palette->Entries[i]; distance=abs(b-(color & 0xff)) + abs(g-(color>>8 & 0xff)) + abs(r-(color>>16 & 0xff)) + abs(a-(color>>24 & 0xff)); if (distanceimage.palette); break; case PixelFormat4bppIndexed: - setpixel_4bppIndexed(r,g,b,a,row,x,bitmap); + setpixel_4bppIndexed(r,g,b,a,row,x,bitmap->image.palette); break; case PixelFormat1bppIndexed: - setpixel_1bppIndexed(r,g,b,a,row,x,bitmap); + setpixel_1bppIndexed(r,g,b,a,row,x,bitmap->image.palette); break; default: FIXME("not implemented for format 0x%x\n", bitmap->format); @@ -500,7 +572,8 @@ GpStatus WINGDIPAPI GdipBitmapSetPixel(GpBitmap* bitmap, INT x, INT y, GpStatus convert_pixels(INT width, INT height, INT dst_stride, BYTE *dst_bits, PixelFormat dst_format, - INT src_stride, const BYTE *src_bits, PixelFormat src_format, ARGB *src_palette) + INT src_stride, const BYTE *src_bits, PixelFormat src_format, + ColorPalette *palette) { INT x, y; @@ -517,9 +590,10 @@ GpStatus convert_pixels(INT width, INT height, for (x=0; xCount) ? palette->Entries[index] : 0; \ setpixel_function(color[2], color[1], color[0], color[3], dst_bits+dst_stride*y, x); \ } \ return Ok; \ @@ -535,6 +609,16 @@ GpStatus convert_pixels(INT width, INT height, return Ok; \ } while (0); +#define convert_rgb_to_indexed(getpixel_function, setpixel_function) do { \ + for (x=0; xScan0 = bitmap->bits + (bitspp / 8) * act_rect.X + bitmap->stride * act_rect.Y; - bitmap->lockmode = flags; + bitmap->lockmode = flags | ImageLockModeRead; bitmap->numlocks++; return Ok; @@ -1011,7 +1139,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect, lockeddata->Stride, lockeddata->Scan0, format, bitmap->stride, bitmap->bits + bitmap->stride * act_rect.Y + PIXELFORMATBPP(bitmap->format) * act_rect.X / 8, - bitmap->format, bitmap->image.palette_entries); + bitmap->format, bitmap->image.palette); if (stat != Ok) { @@ -1021,7 +1149,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect, } } - bitmap->lockmode = flags; + bitmap->lockmode = flags | ImageLockModeRead; bitmap->numlocks++; bitmap->lockx = act_rect.X; bitmap->locky = act_rect.Y; @@ -1120,10 +1248,10 @@ GpStatus WINGDIPAPI GdipCloneBitmapArea(REAL x, REAL y, REAL width, REAL height, if (format == PixelFormatDontCare) format = srcBitmap->format; - area.X = roundr(x); - area.Y = roundr(y); - area.Width = roundr(width); - area.Height = roundr(height); + area.X = gdip_round(x); + area.Y = gdip_round(y); + area.Width = gdip_round(width); + area.Height = gdip_round(height); stat = GdipBitmapLockBits(srcBitmap, &area, ImageLockModeRead, format, &lockeddata_src); @@ -1254,10 +1382,40 @@ GpStatus WINGDIPAPI GdipCloneImage(GpImage *image, GpImage **cloneImage) return stat; } + else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf) + { + GpMetafile *result, *metafile; + + metafile = (GpMetafile*)image; + + result = GdipAlloc(sizeof(*result)); + if (!result) + return OutOfMemory; + + result->image.type = ImageTypeMetafile; + result->image.format = image->format; + result->image.flags = image->flags; + result->image.frame_count = 1; + result->image.xres = image->xres; + result->image.yres = image->yres; + result->bounds = metafile->bounds; + result->unit = metafile->unit; + result->metafile_type = metafile->metafile_type; + result->hemf = CopyEnhMetaFileW(metafile->hemf, NULL); + + if (!result->hemf) + { + GdipFree(result); + return OutOfMemory; + } + + *cloneImage = &result->image; + return Ok; + } else { - ERR("GpImage with no IPicture or bitmap?!\n"); - return NotImplemented; + WARN("GpImage with no image data (metafile in wrong state?)\n"); + return InvalidParameter; } } @@ -1767,21 +1925,24 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride, (*bitmap)->image.type = ImageTypeBitmap; memcpy(&(*bitmap)->image.format, &ImageFormatMemoryBMP, sizeof(GUID)); (*bitmap)->image.flags = ImageFlagsNone; - (*bitmap)->image.palette_flags = 0; - (*bitmap)->image.palette_count = 0; - (*bitmap)->image.palette_size = 0; - (*bitmap)->image.palette_entries = NULL; + (*bitmap)->image.frame_count = 1; + (*bitmap)->image.current_frame = 0; + (*bitmap)->image.palette = NULL; (*bitmap)->image.xres = xres; (*bitmap)->image.yres = yres; (*bitmap)->width = width; (*bitmap)->height = height; (*bitmap)->format = format; (*bitmap)->image.picture = NULL; + (*bitmap)->image.stream = NULL; (*bitmap)->hbitmap = hbitmap; (*bitmap)->hdc = NULL; (*bitmap)->bits = bits; (*bitmap)->stride = stride; (*bitmap)->own_bits = own_bits; + (*bitmap)->metadata_reader = NULL; + (*bitmap)->prop_count = 0; + (*bitmap)->prop_item = NULL; /* set format-related flags */ if (format & (PixelFormatAlpha|PixelFormatPAlpha|PixelFormatIndexed)) @@ -1791,29 +1952,30 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width, INT height, INT stride, format == PixelFormat4bppIndexed || format == PixelFormat8bppIndexed) { - (*bitmap)->image.palette_size = (*bitmap)->image.palette_count = 1 << PIXELFORMATBPP(format); - (*bitmap)->image.palette_entries = GdipAlloc(sizeof(ARGB) * ((*bitmap)->image.palette_size)); + (*bitmap)->image.palette = GdipAlloc(sizeof(UINT) * 2 + sizeof(ARGB) * (1 << PIXELFORMATBPP(format))); - if (!(*bitmap)->image.palette_entries) + if (!(*bitmap)->image.palette) { GdipDisposeImage(&(*bitmap)->image); *bitmap = NULL; return OutOfMemory; } + (*bitmap)->image.palette->Count = 1 << PIXELFORMATBPP(format); + if (format == PixelFormat1bppIndexed) { - (*bitmap)->image.palette_flags = PaletteFlagsGrayScale; - (*bitmap)->image.palette_entries[0] = 0xff000000; - (*bitmap)->image.palette_entries[1] = 0xffffffff; + (*bitmap)->image.palette->Flags = PaletteFlagsGrayScale; + (*bitmap)->image.palette->Entries[0] = 0xff000000; + (*bitmap)->image.palette->Entries[1] = 0xffffffff; } else { if (format == PixelFormat8bppIndexed) - (*bitmap)->image.palette_flags = PaletteFlagsHalftone; + (*bitmap)->image.palette->Flags = PaletteFlagsHalftone; - generate_halftone_palette((*bitmap)->image.palette_entries, - (*bitmap)->image.palette_count); + generate_halftone_palette((*bitmap)->image.palette->Entries, + (*bitmap)->image.palette->Count); } } @@ -1964,19 +2126,21 @@ GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16, * and free src. */ static void move_bitmap(GpBitmap *dst, GpBitmap *src, BOOL clobber_palette) { + assert(src->image.type == ImageTypeBitmap); + assert(dst->image.type == ImageTypeBitmap); + GdipFree(dst->bitmapbits); + GdipFree(dst->own_bits); DeleteDC(dst->hdc); DeleteObject(dst->hbitmap); if (clobber_palette) { - GdipFree(dst->image.palette_entries); - dst->image.palette_flags = src->image.palette_flags; - dst->image.palette_count = src->image.palette_count; - dst->image.palette_entries = src->image.palette_entries; + GdipFree(dst->image.palette); + dst->image.palette = src->image.palette; } else - GdipFree(src->image.palette_entries); + GdipFree(src->image.palette); dst->image.xres = src->image.xres; dst->image.yres = src->image.yres; @@ -1988,14 +2152,25 @@ static void move_bitmap(GpBitmap *dst, GpBitmap *src, BOOL clobber_palette) dst->bits = src->bits; dst->stride = src->stride; dst->own_bits = src->own_bits; + if (dst->metadata_reader) + IWICMetadataReader_Release(dst->metadata_reader); + dst->metadata_reader = src->metadata_reader; + GdipFree(dst->prop_item); + dst->prop_item = src->prop_item; + dst->prop_count = src->prop_count; + if (dst->image.stream) + IStream_Release(dst->image.stream); + dst->image.stream = src->image.stream; + dst->image.frame_count = src->image.frame_count; + dst->image.current_frame = src->image.current_frame; + dst->image.format = src->image.format; + src->image.type = ~0; GdipFree(src); } -GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image) +static GpStatus free_image_data(GpImage *image) { - TRACE("%p\n", image); - if(!image) return InvalidParameter; @@ -2005,13 +2180,17 @@ GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image) GdipFree(((GpBitmap*)image)->own_bits); DeleteDC(((GpBitmap*)image)->hdc); DeleteObject(((GpBitmap*)image)->hbitmap); + if (((GpBitmap*)image)->metadata_reader) + IWICMetadataReader_Release(((GpBitmap*)image)->metadata_reader); + GdipFree(((GpBitmap*)image)->prop_item); } else if (image->type == ImageTypeMetafile) { GpMetafile *metafile = (GpMetafile*)image; GdipFree(metafile->comment_data); DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc)); - DeleteEnhMetaFile(metafile->hemf); + if (!metafile->preserve_hemf) + DeleteEnhMetaFile(metafile->hemf); if (metafile->record_graphics) { WARN("metafile closed while recording\n"); @@ -2027,7 +2206,21 @@ GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image) } if (image->picture) IPicture_Release(image->picture); - GdipFree(image->palette_entries); + if (image->stream) + IStream_Release(image->stream); + GdipFree(image->palette); + + return Ok; +} + +GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image) +{ + GpStatus status; + + TRACE("%p\n", image); + + status = free_image_data(image); + if (status != Ok) return status; image->type = ~0; GdipFree(image); @@ -2100,18 +2293,9 @@ GpStatus WINGDIPAPI GdipGetImageDimension(GpImage *image, REAL *width, return InvalidParameter; if(image->type == ImageTypeMetafile){ - HDC hdc = GetDC(0); - REAL res = (REAL)GetDeviceCaps(hdc, LOGPIXELSX); - - ReleaseDC(0, hdc); - - *height = convert_unit(res, ((GpMetafile*)image)->unit) * - ((GpMetafile*)image)->bounds.Height; - - *width = convert_unit(res, ((GpMetafile*)image)->unit) * - ((GpMetafile*)image)->bounds.Width; + *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit, image->yres); + *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit, image->xres); } - else if(image->type == ImageTypeBitmap){ *height = ((GpBitmap*)image)->height; *width = ((GpBitmap*)image)->width; @@ -2149,7 +2333,11 @@ GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image, stat = GdipCreateFromHDC(hdc, graphics); if (stat == Ok) + { (*graphics)->image = image; + (*graphics)->xres = image->xres; + (*graphics)->yres = image->yres; + } } else if (image->type == ImageTypeMetafile) stat = METAFILE_GetGraphicsContext((GpMetafile*)image, graphics); @@ -2166,15 +2354,8 @@ GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height) if(!image || !height) return InvalidParameter; - if(image->type == ImageTypeMetafile){ - HDC hdc = GetDC(0); - REAL res = (REAL)GetDeviceCaps(hdc, LOGPIXELSX); - - ReleaseDC(0, hdc); - - *height = roundr(convert_unit(res, ((GpMetafile*)image)->unit) * - ((GpMetafile*)image)->bounds.Height); - } + if(image->type == ImageTypeMetafile) + *height = units_to_pixels(((GpMetafile*)image)->bounds.Height, ((GpMetafile*)image)->unit, image->yres); else if(image->type == ImageTypeBitmap) *height = ((GpBitmap*)image)->height; else @@ -2204,10 +2385,10 @@ GpStatus WINGDIPAPI GdipGetImagePaletteSize(GpImage *image, INT *size) if(!image || !size) return InvalidParameter; - if (image->palette_count == 0) + if (!image->palette || image->palette->Count == 0) *size = sizeof(ColorPalette); else - *size = sizeof(UINT)*2 + sizeof(ARGB)*image->palette_count; + *size = sizeof(UINT)*2 + sizeof(ARGB)*image->palette->Count; TRACE("<-- %u\n", *size); @@ -2273,15 +2454,8 @@ GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width) if(!image || !width) return InvalidParameter; - if(image->type == ImageTypeMetafile){ - HDC hdc = GetDC(0); - REAL res = (REAL)GetDeviceCaps(hdc, LOGPIXELSX); - - ReleaseDC(0, hdc); - - *width = roundr(convert_unit(res, ((GpMetafile*)image)->unit) * - ((GpMetafile*)image)->bounds.Width); - } + if(image->type == ImageTypeMetafile) + *width = units_to_pixels(((GpMetafile*)image)->bounds.Width, ((GpMetafile*)image)->unit, image->xres); else if(image->type == ImageTypeBitmap) *width = ((GpBitmap*)image)->width; else @@ -2362,83 +2536,492 @@ GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream, return Ok; } -GpStatus WINGDIPAPI GdipGetAllPropertyItems(GpImage *image, UINT size, - UINT num, PropertyItem* items) +GpStatus WINGDIPAPI GdipGetPropertyCount(GpImage *image, UINT *num) { - static int calls; - - TRACE("(%p, %u, %u, %p)\n", image, size, num, items); - - if(!(calls++)) - FIXME("not implemented\n"); - - return InvalidParameter; -} - -GpStatus WINGDIPAPI GdipGetPropertyCount(GpImage *image, UINT* num) -{ - static int calls; - TRACE("(%p, %p)\n", image, num); - if(!(calls++)) - FIXME("not implemented\n"); + if (!image || !num) return InvalidParameter; *num = 0; + + if (image->type == ImageTypeBitmap) + { + if (((GpBitmap *)image)->prop_item) + { + *num = ((GpBitmap *)image)->prop_count; + return Ok; + } + + if (((GpBitmap *)image)->metadata_reader) + IWICMetadataReader_GetCount(((GpBitmap *)image)->metadata_reader, num); + } + return Ok; } -GpStatus WINGDIPAPI GdipGetPropertyIdList(GpImage *image, UINT num, PROPID* list) +GpStatus WINGDIPAPI GdipGetPropertyIdList(GpImage *image, UINT num, PROPID *list) { - static int calls; + HRESULT hr; + IWICMetadataReader *reader; + IWICEnumMetadataItem *enumerator; + UINT prop_count, i, items_returned; TRACE("(%p, %u, %p)\n", image, num, list); - if(!(calls++)) - FIXME("not implemented\n"); + if (!image || !list) return InvalidParameter; - return InvalidParameter; + if (image->type != ImageTypeBitmap) + { + FIXME("Not implemented for type %d\n", image->type); + return NotImplemented; + } + + if (((GpBitmap *)image)->prop_item) + { + if (num != ((GpBitmap *)image)->prop_count) return InvalidParameter; + + for (i = 0; i < num; i++) + { + list[i] = ((GpBitmap *)image)->prop_item[i].id; + } + + return Ok; + } + + reader = ((GpBitmap *)image)->metadata_reader; + if (!reader) + { + if (num != 0) return InvalidParameter; + return Ok; + } + + hr = IWICMetadataReader_GetCount(reader, &prop_count); + if (FAILED(hr)) return hresult_to_status(hr); + + if (num != prop_count) return InvalidParameter; + + hr = IWICMetadataReader_GetEnumerator(reader, &enumerator); + if (FAILED(hr)) return hresult_to_status(hr); + + IWICEnumMetadataItem_Reset(enumerator); + + for (i = 0; i < num; i++) + { + PROPVARIANT id; + + hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, NULL, &items_returned); + if (hr != S_OK) break; + + if (id.vt != VT_UI2) + { + FIXME("not supported propvariant type for id: %u\n", id.vt); + list[i] = 0; + continue; + } + list[i] = id.u.uiVal; + } + + IWICEnumMetadataItem_Release(enumerator); + + return hr == S_OK ? Ok : hresult_to_status(hr); } -GpStatus WINGDIPAPI GdipGetPropertyItem(GpImage *image, PROPID id, UINT size, - PropertyItem* buffer) +static UINT propvariant_size(PROPVARIANT *value) { - static int calls; - - TRACE("(%p, %u, %u, %p)\n", image, id, size, buffer); - - if(!(calls++)) - FIXME("not implemented\n"); - - return InvalidParameter; + switch (value->vt & ~VT_VECTOR) + { + case VT_EMPTY: + return 0; + case VT_I1: + case VT_UI1: + if (!(value->vt & VT_VECTOR)) return 1; + return value->u.caub.cElems; + case VT_I2: + case VT_UI2: + if (!(value->vt & VT_VECTOR)) return 2; + return value->u.caui.cElems * 2; + case VT_I4: + case VT_UI4: + case VT_R4: + if (!(value->vt & VT_VECTOR)) return 4; + return value->u.caul.cElems * 4; + case VT_I8: + case VT_UI8: + case VT_R8: + if (!(value->vt & VT_VECTOR)) return 8; + return value->u.cauh.cElems * 8; + case VT_LPSTR: + return value->u.pszVal ? strlen(value->u.pszVal) + 1 : 0; + case VT_BLOB: + return value->u.blob.cbSize; + default: + FIXME("not supported variant type %d\n", value->vt); + return 0; + } } -GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID pid, - UINT* size) +GpStatus WINGDIPAPI GdipGetPropertyItemSize(GpImage *image, PROPID propid, UINT *size) { - static int calls; + HRESULT hr; + IWICMetadataReader *reader; + PROPVARIANT id, value; - TRACE("%p %x %p\n", image, pid, size); + TRACE("(%p,%#x,%p)\n", image, propid, size); - if(!size || !image) + if (!size || !image) return InvalidParameter; + + if (image->type != ImageTypeBitmap) + { + FIXME("Not implemented for type %d\n", image->type); + return NotImplemented; + } + + if (((GpBitmap *)image)->prop_item) + { + UINT i; + + for (i = 0; i < ((GpBitmap *)image)->prop_count; i++) + { + if (propid == ((GpBitmap *)image)->prop_item[i].id) + { + *size = sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length; + return Ok; + } + } + + return PropertyNotFound; + } + + reader = ((GpBitmap *)image)->metadata_reader; + if (!reader) return PropertyNotFound; + + id.vt = VT_UI2; + id.u.uiVal = propid; + hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value); + if (FAILED(hr)) return PropertyNotFound; + + *size = propvariant_size(&value); + if (*size) *size += sizeof(PropertyItem); + PropVariantClear(&value); + + return Ok; +} + +#ifndef PropertyTagTypeSByte +#define PropertyTagTypeSByte 6 +#define PropertyTagTypeSShort 8 +#define PropertyTagTypeFloat 11 +#define PropertyTagTypeDouble 12 +#endif + +static UINT vt_to_itemtype(UINT vt) +{ + static const struct + { + UINT vt, type; + } vt2type[] = + { + { VT_I1, PropertyTagTypeSByte }, + { VT_UI1, PropertyTagTypeByte }, + { VT_I2, PropertyTagTypeSShort }, + { VT_UI2, PropertyTagTypeShort }, + { VT_I4, PropertyTagTypeSLONG }, + { VT_UI4, PropertyTagTypeLong }, + { VT_I8, PropertyTagTypeSRational }, + { VT_UI8, PropertyTagTypeRational }, + { VT_R4, PropertyTagTypeFloat }, + { VT_R8, PropertyTagTypeDouble }, + { VT_LPSTR, PropertyTagTypeASCII }, + { VT_BLOB, PropertyTagTypeUndefined } + }; + UINT i; + for (i = 0; i < sizeof(vt2type)/sizeof(vt2type[0]); i++) + { + if (vt2type[i].vt == vt) return vt2type[i].type; + } + FIXME("not supported variant type %u\n", vt); + return 0; +} + +static GpStatus propvariant_to_item(PROPVARIANT *value, PropertyItem *item, + UINT size, PROPID id) +{ + UINT item_size, item_type; + + item_size = propvariant_size(value); + if (size != item_size + sizeof(PropertyItem)) return InvalidParameter; + + item_type = vt_to_itemtype(value->vt & ~VT_VECTOR); + if (!item_type) return InvalidParameter; + + item->value = item + 1; + + switch (value->vt & ~VT_VECTOR) + { + case VT_I1: + case VT_UI1: + if (!(value->vt & VT_VECTOR)) + *(BYTE *)item->value = value->u.bVal; + else + memcpy(item->value, value->u.caub.pElems, item_size); + break; + case VT_I2: + case VT_UI2: + if (!(value->vt & VT_VECTOR)) + *(USHORT *)item->value = value->u.uiVal; + else + memcpy(item->value, value->u.caui.pElems, item_size); + break; + case VT_I4: + case VT_UI4: + case VT_R4: + if (!(value->vt & VT_VECTOR)) + *(ULONG *)item->value = value->u.ulVal; + else + memcpy(item->value, value->u.caul.pElems, item_size); + break; + case VT_I8: + case VT_UI8: + case VT_R8: + if (!(value->vt & VT_VECTOR)) + *(ULONGLONG *)item->value = value->u.uhVal.QuadPart; + else + memcpy(item->value, value->u.cauh.pElems, item_size); + break; + case VT_LPSTR: + memcpy(item->value, value->u.pszVal, item_size); + break; + case VT_BLOB: + memcpy(item->value, value->u.blob.pBlobData, item_size); + break; + default: + FIXME("not supported variant type %d\n", value->vt); return InvalidParameter; + } - if(!(calls++)) - FIXME("not implemented\n"); + item->length = item_size; + item->type = item_type; + item->id = id; - return NotImplemented; + return Ok; } -GpStatus WINGDIPAPI GdipGetPropertySize(GpImage *image, UINT* size, UINT* num) +GpStatus WINGDIPAPI GdipGetPropertyItem(GpImage *image, PROPID propid, UINT size, + PropertyItem *buffer) { - static int calls; + GpStatus stat; + HRESULT hr; + IWICMetadataReader *reader; + PROPVARIANT id, value; - TRACE("(%p,%p,%p)\n", image, size, num); + TRACE("(%p,%#x,%u,%p)\n", image, propid, size, buffer); - if(!(calls++)) - FIXME("not implemented\n"); + if (!image || !buffer) return InvalidParameter; - return InvalidParameter; + if (image->type != ImageTypeBitmap) + { + FIXME("Not implemented for type %d\n", image->type); + return NotImplemented; + } + + if (((GpBitmap *)image)->prop_item) + { + UINT i; + + for (i = 0; i < ((GpBitmap *)image)->prop_count; i++) + { + if (propid == ((GpBitmap *)image)->prop_item[i].id) + { + if (size != sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length) + return InvalidParameter; + + *buffer = ((GpBitmap *)image)->prop_item[i]; + buffer->value = buffer + 1; + memcpy(buffer->value, ((GpBitmap *)image)->prop_item[i].value, buffer->length); + return Ok; + } + } + + return PropertyNotFound; + } + + reader = ((GpBitmap *)image)->metadata_reader; + if (!reader) return PropertyNotFound; + + id.vt = VT_UI2; + id.u.uiVal = propid; + hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value); + if (FAILED(hr)) return PropertyNotFound; + + stat = propvariant_to_item(&value, buffer, size, propid); + PropVariantClear(&value); + + return stat; +} + +GpStatus WINGDIPAPI GdipGetPropertySize(GpImage *image, UINT *size, UINT *count) +{ + HRESULT hr; + IWICMetadataReader *reader; + IWICEnumMetadataItem *enumerator; + UINT prop_count, prop_size, i; + PROPVARIANT id, value; + + TRACE("(%p,%p,%p)\n", image, size, count); + + if (!image || !size || !count) return InvalidParameter; + + if (image->type != ImageTypeBitmap) + { + FIXME("Not implemented for type %d\n", image->type); + return NotImplemented; + } + + if (((GpBitmap *)image)->prop_item) + { + *count = ((GpBitmap *)image)->prop_count; + *size = 0; + + for (i = 0; i < ((GpBitmap *)image)->prop_count; i++) + { + *size += sizeof(PropertyItem) + ((GpBitmap *)image)->prop_item[i].length; + } + + return Ok; + } + + reader = ((GpBitmap *)image)->metadata_reader; + if (!reader) return PropertyNotFound; + + hr = IWICMetadataReader_GetCount(reader, &prop_count); + if (FAILED(hr)) return hresult_to_status(hr); + + hr = IWICMetadataReader_GetEnumerator(reader, &enumerator); + if (FAILED(hr)) return hresult_to_status(hr); + + IWICEnumMetadataItem_Reset(enumerator); + + prop_size = 0; + + PropVariantInit(&id); + PropVariantInit(&value); + + for (i = 0; i < prop_count; i++) + { + UINT items_returned, item_size; + + hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned); + if (hr != S_OK) break; + + item_size = propvariant_size(&value); + if (item_size) prop_size += sizeof(PropertyItem) + item_size; + + PropVariantClear(&id); + PropVariantClear(&value); + } + + IWICEnumMetadataItem_Release(enumerator); + + if (hr != S_OK) return PropertyNotFound; + + *count = prop_count; + *size = prop_size; + return Ok; +} + +GpStatus WINGDIPAPI GdipGetAllPropertyItems(GpImage *image, UINT size, + UINT count, PropertyItem *buf) +{ + GpStatus status; + HRESULT hr; + IWICMetadataReader *reader; + IWICEnumMetadataItem *enumerator; + UINT prop_count, prop_size, i; + PROPVARIANT id, value; + char *item_value; + + TRACE("(%p,%u,%u,%p)\n", image, size, count, buf); + + if (!image || !buf) return InvalidParameter; + + if (image->type != ImageTypeBitmap) + { + FIXME("Not implemented for type %d\n", image->type); + return NotImplemented; + } + + status = GdipGetPropertySize(image, &prop_size, &prop_count); + if (status != Ok) return status; + + if (prop_count != count || prop_size != size) return InvalidParameter; + + if (((GpBitmap *)image)->prop_item) + { + memcpy(buf, ((GpBitmap *)image)->prop_item, prop_size); + + item_value = (char *)(buf + prop_count); + + for (i = 0; i < prop_count; i++) + { + buf[i].value = item_value; + item_value += buf[i].length; + } + + return Ok; + } + + reader = ((GpBitmap *)image)->metadata_reader; + if (!reader) return PropertyNotFound; + + hr = IWICMetadataReader_GetEnumerator(reader, &enumerator); + if (FAILED(hr)) return hresult_to_status(hr); + + IWICEnumMetadataItem_Reset(enumerator); + + item_value = (char *)(buf + prop_count); + + PropVariantInit(&id); + PropVariantInit(&value); + + for (i = 0; i < prop_count; i++) + { + PropertyItem *item; + UINT items_returned, item_size; + + hr = IWICEnumMetadataItem_Next(enumerator, 1, NULL, &id, &value, &items_returned); + if (hr != S_OK) break; + + if (id.vt != VT_UI2) + { + FIXME("not supported propvariant type for id: %u\n", id.vt); + continue; + } + + item_size = propvariant_size(&value); + if (item_size) + { + item = HeapAlloc(GetProcessHeap(), 0, item_size + sizeof(*item)); + + propvariant_to_item(&value, item, item_size + sizeof(*item), id.u.uiVal); + buf[i].id = item->id; + buf[i].type = item->type; + buf[i].length = item_size; + buf[i].value = item_value; + memcpy(item_value, item->value, item_size); + item_value += item_size; + + HeapFree(GetProcessHeap(), 0, item); + } + + PropVariantClear(&id); + PropVariantClear(&value); + } + + IWICEnumMetadataItem_Release(enumerator); + + if (hr != S_OK) return PropertyNotFound; + + return Ok; } struct image_format_dimension @@ -2447,30 +3030,31 @@ struct image_format_dimension const GUID *dimension; }; -static struct image_format_dimension image_format_dimensions[] = +static const struct image_format_dimension image_format_dimensions[] = { {&ImageFormatGIF, &FrameDimensionTime}, {&ImageFormatIcon, &FrameDimensionResolution}, {NULL} }; -/* FIXME: Need to handle multi-framed images */ GpStatus WINGDIPAPI GdipImageGetFrameCount(GpImage *image, GDIPCONST GUID* dimensionID, UINT* count) { - static int calls; - TRACE("(%p,%s,%p)\n", image, debugstr_guid(dimensionID), count); if(!image || !count) return InvalidParameter; - if(!(calls++)) - FIXME("returning frame count of 1\n"); + if (!dimensionID || + IsEqualGUID(dimensionID, &image->format) || + IsEqualGUID(dimensionID, &FrameDimensionPage) || + IsEqualGUID(dimensionID, &FrameDimensionTime)) + { + *count = image->frame_count; + return Ok; + } - *count = 1; - - return Ok; + return InvalidParameter; } GpStatus WINGDIPAPI GdipImageGetFrameDimensionsCount(GpImage *image, @@ -2516,22 +3100,6 @@ GpStatus WINGDIPAPI GdipImageGetFrameDimensionsList(GpImage* image, return Ok; } -GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, - GDIPCONST GUID* dimensionID, UINT frameidx) -{ - static int calls; - - TRACE("(%p, %s, %u)\n", image, debugstr_guid(dimensionID), frameidx); - - if(!image || !dimensionID) - return InvalidParameter; - - if(!(calls++)) - FIXME("not implemented\n"); - - return Ok; -} - GpStatus WINGDIPAPI GdipLoadImageFromFile(GDIPCONST WCHAR* filename, GpImage **image) { @@ -2563,24 +3131,419 @@ GpStatus WINGDIPAPI GdipLoadImageFromFileICM(GDIPCONST WCHAR* filename,GpImage * return GdipLoadImageFromFile(filename, image); } -static const WICPixelFormatGUID *wic_pixel_formats[] = { - &GUID_WICPixelFormat16bppBGR555, - &GUID_WICPixelFormat24bppBGR, - &GUID_WICPixelFormat32bppBGR, - &GUID_WICPixelFormat32bppBGRA, - &GUID_WICPixelFormat32bppPBGRA, - NULL -}; +static void add_property(GpBitmap *bitmap, PropertyItem *item) +{ + UINT prop_size, prop_count; + PropertyItem *prop_item; -static const PixelFormat wic_gdip_formats[] = { - PixelFormat16bppRGB555, - PixelFormat24bppRGB, - PixelFormat32bppRGB, - PixelFormat32bppARGB, - PixelFormat32bppPARGB, -}; + if (bitmap->prop_item == NULL) + { + prop_size = prop_count = 0; + prop_item = GdipAlloc(item->length + sizeof(PropertyItem)); + if (!prop_item) return; + } + else + { + UINT i; + char *item_value; -static GpStatus decode_image_wic(IStream* stream, REFCLSID clsid, GpImage **image) + GdipGetPropertySize((GpImage *)bitmap, &prop_size, &prop_count); + + prop_item = GdipAlloc(prop_size + item->length + sizeof(PropertyItem)); + if (!prop_item) return; + memcpy(prop_item, bitmap->prop_item, sizeof(PropertyItem) * bitmap->prop_count); + prop_size -= sizeof(PropertyItem) * bitmap->prop_count; + memcpy(prop_item + prop_count + 1, bitmap->prop_item + prop_count, prop_size); + + item_value = (char *)(prop_item + prop_count + 1); + + for (i = 0; i < prop_count; i++) + { + prop_item[i].value = item_value; + item_value += prop_item[i].length; + } + } + + prop_item[prop_count].id = item->id; + prop_item[prop_count].type = item->type; + prop_item[prop_count].length = item->length; + prop_item[prop_count].value = (char *)(prop_item + prop_count + 1) + prop_size; + memcpy(prop_item[prop_count].value, item->value, item->length); + + GdipFree(bitmap->prop_item); + bitmap->prop_item = prop_item; + bitmap->prop_count++; +} + +static BOOL get_bool_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name) +{ + HRESULT hr; + GUID format; + PROPVARIANT id, value; + BOOL ret = FALSE; + + IWICMetadataReader_GetMetadataFormat(reader, &format); + if (!IsEqualGUID(&format, guid)) return FALSE; + + PropVariantInit(&id); + PropVariantInit(&value); + + id.vt = VT_LPWSTR; + id.u.pwszVal = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(prop_name) + 1) * sizeof(WCHAR)); + if (!id.u.pwszVal) return FALSE; + lstrcpyW(id.u.pwszVal, prop_name); + hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value); + if (hr == S_OK && value.vt == VT_BOOL) + ret = value.u.boolVal; + + PropVariantClear(&id); + PropVariantClear(&value); + + return ret; +} + +static PropertyItem *get_property(IWICMetadataReader *reader, const GUID *guid, const WCHAR *prop_name) +{ + HRESULT hr; + GUID format; + PROPVARIANT id, value; + PropertyItem *item = NULL; + + IWICMetadataReader_GetMetadataFormat(reader, &format); + if (!IsEqualGUID(&format, guid)) return NULL; + + PropVariantInit(&id); + PropVariantInit(&value); + + id.vt = VT_LPWSTR; + id.u.pwszVal = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(prop_name) + 1) * sizeof(WCHAR)); + if (!id.u.pwszVal) return NULL; + lstrcpyW(id.u.pwszVal, prop_name); + hr = IWICMetadataReader_GetValue(reader, NULL, &id, &value); + if (hr == S_OK) + { + UINT item_size = propvariant_size(&value); + if (item_size) + { + item_size += sizeof(*item); + item = GdipAlloc(item_size); + if (propvariant_to_item(&value, item, item_size, 0) != Ok) + { + GdipFree(item); + item = NULL; + } + } + } + + PropVariantClear(&id); + PropVariantClear(&value); + + return item; +} + +static PropertyItem *get_gif_comment(IWICMetadataReader *reader) +{ + static const WCHAR textentryW[] = { 'T','e','x','t','E','n','t','r','y',0 }; + PropertyItem *comment; + + comment = get_property(reader, &GUID_MetadataFormatGifComment, textentryW); + if (comment) + comment->id = PropertyTagExifUserComment; + + return comment; +} + +static PropertyItem *get_gif_loopcount(IWICMetadataReader *reader) +{ + static const WCHAR applicationW[] = { 'A','p','p','l','i','c','a','t','i','o','n',0 }; + static const WCHAR dataW[] = { 'D','a','t','a',0 }; + PropertyItem *appext = NULL, *appdata = NULL, *loop = NULL; + + appext = get_property(reader, &GUID_MetadataFormatAPE, applicationW); + if (appext) + { + if (appext->type == PropertyTagTypeByte && appext->length == 11 && + (!memcmp(appext->value, "NETSCAPE2.0", 11) || !memcmp(appext->value, "ANIMEXTS1.0", 11))) + { + appdata = get_property(reader, &GUID_MetadataFormatAPE, dataW); + if (appdata) + { + if (appdata->type == PropertyTagTypeByte && appdata->length == 4) + { + BYTE *data = appdata->value; + if (data[0] == 3 && data[1] == 1) + { + loop = GdipAlloc(sizeof(*loop) + sizeof(SHORT)); + if (loop) + { + loop->type = PropertyTagTypeShort; + loop->id = PropertyTagLoopCount; + loop->length = sizeof(SHORT); + loop->value = loop + 1; + *(SHORT *)loop->value = data[2] | (data[3] << 8); + } + } + } + } + } + } + + GdipFree(appext); + GdipFree(appdata); + + return loop; +} + +static PropertyItem *get_gif_background(IWICMetadataReader *reader) +{ + static const WCHAR backgroundW[] = { 'B','a','c','k','g','r','o','u','n','d','C','o','l','o','r','I','n','d','e','x',0 }; + PropertyItem *background; + + background = get_property(reader, &GUID_MetadataFormatLSD, backgroundW); + if (background) + background->id = PropertyTagIndexBackground; + + return background; +} + +static PropertyItem *get_gif_palette(IWICBitmapDecoder *decoder, IWICMetadataReader *reader) +{ + static const WCHAR global_flagW[] = { 'G','l','o','b','a','l','C','o','l','o','r','T','a','b','l','e','F','l','a','g',0 }; + HRESULT hr; + IWICImagingFactory *factory; + IWICPalette *palette; + UINT count = 0; + WICColor colors[256]; + + if (!get_bool_property(reader, &GUID_MetadataFormatLSD, global_flagW)) + return NULL; + + hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, + &IID_IWICImagingFactory, (void **)&factory); + if (hr != S_OK) return NULL; + + hr = IWICImagingFactory_CreatePalette(factory, &palette); + if (hr == S_OK) + { + hr = IWICBitmapDecoder_CopyPalette(decoder, palette); + if (hr == S_OK) + IWICPalette_GetColors(palette, 256, colors, &count); + + IWICPalette_Release(palette); + } + + IWICImagingFactory_Release(factory); + + if (count) + { + PropertyItem *pal; + UINT i; + BYTE *rgb; + + pal = GdipAlloc(sizeof(*pal) + count * 3); + if (!pal) return NULL; + pal->type = PropertyTagTypeByte; + pal->id = PropertyTagGlobalPalette; + pal->value = pal + 1; + pal->length = count * 3; + + rgb = pal->value; + + for (i = 0; i < count; i++) + { + rgb[i*3] = (colors[i] >> 16) & 0xff; + rgb[i*3 + 1] = (colors[i] >> 8) & 0xff; + rgb[i*3 + 2] = colors[i] & 0xff; + } + + return pal; + } + + return NULL; +} + +static PropertyItem *get_gif_transparent_idx(IWICMetadataReader *reader) +{ + static const WCHAR transparency_flagW[] = { 'T','r','a','n','s','p','a','r','e','n','c','y','F','l','a','g',0 }; + static const WCHAR colorW[] = { 'T','r','a','n','s','p','a','r','e','n','t','C','o','l','o','r','I','n','d','e','x',0 }; + PropertyItem *index = NULL; + + if (get_bool_property(reader, &GUID_MetadataFormatGCE, transparency_flagW)) + { + index = get_property(reader, &GUID_MetadataFormatGCE, colorW); + if (index) + index->id = PropertyTagIndexTransparent; + } + return index; +} + +static LONG get_gif_frame_delay(IWICBitmapFrameDecode *frame) +{ + static const WCHAR delayW[] = { 'D','e','l','a','y',0 }; + HRESULT hr; + IWICMetadataBlockReader *block_reader; + IWICMetadataReader *reader; + UINT block_count, i; + PropertyItem *delay; + LONG value = 0; + + hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader); + if (hr == S_OK) + { + hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count); + if (hr == S_OK) + { + for (i = 0; i < block_count; i++) + { + hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader); + if (hr == S_OK) + { + delay = get_property(reader, &GUID_MetadataFormatGCE, delayW); + if (delay) + { + if (delay->type == PropertyTagTypeShort && delay->length == 2) + value = *(SHORT *)delay->value; + + GdipFree(delay); + } + IWICMetadataReader_Release(reader); + } + } + } + IWICMetadataBlockReader_Release(block_reader); + } + + return value; +} + +static void gif_metadata_reader(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT active_frame) +{ + HRESULT hr; + IWICBitmapFrameDecode *frame; + IWICMetadataBlockReader *block_reader; + IWICMetadataReader *reader; + UINT frame_count, block_count, i; + PropertyItem *delay = NULL, *comment = NULL, *background = NULL; + PropertyItem *transparent_idx = NULL, *loop = NULL, *palette = NULL; + + IWICBitmapDecoder_GetFrameCount(decoder, &frame_count); + if (frame_count > 1) + { + delay = GdipAlloc(sizeof(*delay) + frame_count * sizeof(LONG)); + if (delay) + { + LONG *value; + + delay->type = PropertyTagTypeLong; + delay->id = PropertyTagFrameDelay; + delay->length = frame_count * sizeof(LONG); + delay->value = delay + 1; + + value = delay->value; + + for (i = 0; i < frame_count; i++) + { + hr = IWICBitmapDecoder_GetFrame(decoder, i, &frame); + if (hr == S_OK) + { + value[i] = get_gif_frame_delay(frame); + IWICBitmapFrameDecode_Release(frame); + } + else value[i] = 0; + } + } + } + + hr = IWICBitmapDecoder_QueryInterface(decoder, &IID_IWICMetadataBlockReader, (void **)&block_reader); + if (hr == S_OK) + { + hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count); + if (hr == S_OK) + { + for (i = 0; i < block_count; i++) + { + hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader); + if (hr == S_OK) + { + if (!comment) + comment = get_gif_comment(reader); + + if (frame_count > 1 && !loop) + loop = get_gif_loopcount(reader); + + if (!background) + background = get_gif_background(reader); + + if (!palette) + palette = get_gif_palette(decoder, reader); + + IWICMetadataReader_Release(reader); + } + } + } + IWICMetadataBlockReader_Release(block_reader); + } + + if (frame_count > 1 && !loop) + { + loop = GdipAlloc(sizeof(*loop) + sizeof(SHORT)); + if (loop) + { + loop->type = PropertyTagTypeShort; + loop->id = PropertyTagLoopCount; + loop->length = sizeof(SHORT); + loop->value = loop + 1; + *(SHORT *)loop->value = 1; + } + } + + if (delay) add_property(bitmap, delay); + if (comment) add_property(bitmap, comment); + if (loop) add_property(bitmap, loop); + if (palette) add_property(bitmap, palette); + if (background) add_property(bitmap, background); + + GdipFree(delay); + GdipFree(comment); + GdipFree(loop); + GdipFree(palette); + GdipFree(background); + + /* Win7 gdiplus always returns transparent color index from frame 0 */ + hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame); + if (hr != S_OK) return; + + hr = IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader); + if (hr == S_OK) + { + hr = IWICMetadataBlockReader_GetCount(block_reader, &block_count); + if (hr == S_OK) + { + for (i = 0; i < block_count; i++) + { + hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &reader); + if (hr == S_OK) + { + if (!transparent_idx) + transparent_idx = get_gif_transparent_idx(reader); + + IWICMetadataReader_Release(reader); + } + } + } + IWICMetadataBlockReader_Release(block_reader); + } + + if (transparent_idx) add_property(bitmap, transparent_idx); + GdipFree(transparent_idx); + + IWICBitmapFrameDecode_Release(frame); +} + +typedef void (*metadata_reader_func)(GpBitmap *bitmap, IWICBitmapDecoder *decoder, UINT frame); + +static GpStatus decode_image_wic(IStream *stream, GDIPCONST CLSID *clsid, + UINT active_frame, metadata_reader_func metadata_reader, GpImage **image) { GpStatus status=Ok; GpBitmap *bitmap; @@ -2588,23 +3551,31 @@ static GpStatus decode_image_wic(IStream* stream, REFCLSID clsid, GpImage **imag IWICBitmapDecoder *decoder; IWICBitmapFrameDecode *frame; IWICBitmapSource *source=NULL; + IWICMetadataBlockReader *block_reader; WICPixelFormatGUID wic_format; PixelFormat gdip_format=0; + ColorPalette *palette = NULL; + WICBitmapPaletteType palette_type = WICBitmapPaletteTypeFixedHalftone256; int i; - UINT width, height; + UINT width, height, frame_count; BitmapData lockeddata; WICRect wrc; HRESULT initresult; + TRACE("%p,%s,%u,%p\n", stream, wine_dbgstr_guid(clsid), active_frame, image); + initresult = CoInitialize(NULL); hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IWICBitmapDecoder, (void**)&decoder); if (FAILED(hr)) goto end; - hr = IWICBitmapDecoder_Initialize(decoder, (IStream*)stream, WICDecodeMetadataCacheOnLoad); + hr = IWICBitmapDecoder_Initialize(decoder, stream, WICDecodeMetadataCacheOnLoad); if (SUCCEEDED(hr)) - hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame); + { + IWICBitmapDecoder_GetFrameCount(decoder, &frame_count); + hr = IWICBitmapDecoder_GetFrame(decoder, active_frame, &frame); + } if (SUCCEEDED(hr)) /* got frame */ { @@ -2612,22 +3583,27 @@ static GpStatus decode_image_wic(IStream* stream, REFCLSID clsid, GpImage **imag if (SUCCEEDED(hr)) { - for (i=0; wic_pixel_formats[i]; i++) + IWICBitmapSource *bmp_source; + IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICBitmapSource, (void **)&bmp_source); + + for (i=0; pixel_formats[i].wic_format; i++) { - if (IsEqualGUID(&wic_format, wic_pixel_formats[i])) + if (IsEqualGUID(&wic_format, pixel_formats[i].wic_format)) { - source = (IWICBitmapSource*)frame; - IWICBitmapSource_AddRef(source); - gdip_format = wic_gdip_formats[i]; + source = bmp_source; + gdip_format = pixel_formats[i].gdip_format; + palette_type = pixel_formats[i].palette_type; break; } } if (!source) { /* unknown format; fall back on 32bppARGB */ - hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)frame, &source); + hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, bmp_source, &source); gdip_format = PixelFormat32bppARGB; + IWICBitmapSource_Release(bmp_source); } + TRACE("%s => %#x\n", wine_dbgstr_guid(&wic_format), gdip_format); } if (SUCCEEDED(hr)) /* got source */ @@ -2682,7 +3658,22 @@ static GpStatus decode_image_wic(IStream* stream, REFCLSID clsid, GpImage **imag IWICBitmapSource_Release(source); } - IWICBitmapFrameDecode_Release(frame); + if (SUCCEEDED(hr)) { + bitmap->metadata_reader = NULL; + + if (metadata_reader) + metadata_reader(bitmap, decoder, active_frame); + else if (IWICBitmapFrameDecode_QueryInterface(frame, &IID_IWICMetadataBlockReader, (void **)&block_reader) == S_OK) + { + UINT block_count = 0; + if (IWICMetadataBlockReader_GetCount(block_reader, &block_count) == S_OK && block_count) + IWICMetadataBlockReader_GetReaderByIndex(block_reader, 0, &bitmap->metadata_reader); + IWICMetadataBlockReader_Release(block_reader); + } + + palette = get_palette(frame, palette_type); + IWICBitmapFrameDecode_Release(frame); + } } IWICBitmapDecoder_Release(decoder); @@ -2696,22 +3687,38 @@ end: { /* Native GDI+ used to be smarter, but since Win7 it just sets these flags. */ bitmap->image.flags |= ImageFlagsReadOnly|ImageFlagsHasRealPixelSize|ImageFlagsHasRealDPI|ImageFlagsColorSpaceRGB; + bitmap->image.frame_count = frame_count; + bitmap->image.current_frame = active_frame; + bitmap->image.stream = stream; + if (palette) + { + GdipFree(bitmap->image.palette); + bitmap->image.palette = palette; + } + else + { + if (IsEqualGUID(&wic_format, &GUID_WICPixelFormatBlackWhite)) + bitmap->image.palette->Flags = 0; + } + /* Pin the source stream */ + IStream_AddRef(stream); + TRACE("=> %p\n", *image); } return status; } -static GpStatus decode_image_icon(IStream* stream, REFCLSID clsid, GpImage **image) +static GpStatus decode_image_icon(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) { - return decode_image_wic(stream, &CLSID_WICIcoDecoder, image); + return decode_image_wic(stream, &CLSID_WICIcoDecoder, active_frame, NULL, image); } -static GpStatus decode_image_bmp(IStream* stream, REFCLSID clsid, GpImage **image) +static GpStatus decode_image_bmp(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) { GpStatus status; GpBitmap* bitmap; - status = decode_image_wic(stream, &CLSID_WICBmpDecoder, image); + status = decode_image_wic(stream, &CLSID_WICBmpDecoder, active_frame, NULL, image); bitmap = (GpBitmap*)*image; @@ -2724,27 +3731,27 @@ static GpStatus decode_image_bmp(IStream* stream, REFCLSID clsid, GpImage **imag return status; } -static GpStatus decode_image_jpeg(IStream* stream, REFCLSID clsid, GpImage **image) +static GpStatus decode_image_jpeg(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) { - return decode_image_wic(stream, &CLSID_WICJpegDecoder, image); + return decode_image_wic(stream, &CLSID_WICJpegDecoder, active_frame, NULL, image); } -static GpStatus decode_image_png(IStream* stream, REFCLSID clsid, GpImage **image) +static GpStatus decode_image_png(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) { - return decode_image_wic(stream, &CLSID_WICPngDecoder, image); + return decode_image_wic(stream, &CLSID_WICPngDecoder, active_frame, NULL, image); } -static GpStatus decode_image_gif(IStream* stream, REFCLSID clsid, GpImage **image) +static GpStatus decode_image_gif(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) { - return decode_image_wic(stream, &CLSID_WICGifDecoder, image); + return decode_image_wic(stream, &CLSID_WICGifDecoder, active_frame, gif_metadata_reader, image); } -static GpStatus decode_image_tiff(IStream* stream, REFCLSID clsid, GpImage **image) +static GpStatus decode_image_tiff(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) { - return decode_image_wic(stream, &CLSID_WICTiffDecoder, image); + return decode_image_wic(stream, &CLSID_WICTiffDecoder, active_frame, NULL, image); } -static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid, GpImage **image) +static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid, UINT active_frame, GpImage **image) { IPicture *pic; @@ -2763,12 +3770,12 @@ static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid *image = GdipAlloc(sizeof(GpMetafile)); if(!*image) return OutOfMemory; (*image)->type = ImageTypeMetafile; + (*image)->stream = NULL; (*image)->picture = pic; (*image)->flags = ImageFlagsNone; - (*image)->palette_flags = 0; - (*image)->palette_count = 0; - (*image)->palette_size = 0; - (*image)->palette_entries = NULL; + (*image)->frame_count = 1; + (*image)->current_frame = 0; + (*image)->palette = NULL; TRACE("<-- %p\n", *image); @@ -2778,7 +3785,7 @@ static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid typedef GpStatus (*encode_image_func)(GpImage *image, IStream* stream, GDIPCONST CLSID* clsid, GDIPCONST EncoderParameters* params); -typedef GpStatus (*decode_image_func)(IStream *stream, REFCLSID clsid, GpImage** image); +typedef GpStatus (*decode_image_func)(IStream *stream, REFCLSID clsid, UINT active_frame, GpImage **image); typedef struct image_codec { ImageCodecInfo info; @@ -2847,7 +3854,75 @@ static GpStatus get_decoder_info(IStream* stream, const struct image_codec **res return GenericError; } -GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream* stream, GpImage **image) +GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *dimensionID, + UINT frame) +{ + GpStatus stat; + LARGE_INTEGER seek; + HRESULT hr; + const struct image_codec *codec = NULL; + GpImage *new_image; + + TRACE("(%p,%s,%u)\n", image, debugstr_guid(dimensionID), frame); + + if (!image || !dimensionID) + return InvalidParameter; + + if (frame >= image->frame_count) + { + WARN("requested frame %u, but image has only %u\n", frame, image->frame_count); + return InvalidParameter; + } + + if (image->type != ImageTypeBitmap && image->type != ImageTypeMetafile) + { + WARN("invalid image type %d\n", image->type); + return InvalidParameter; + } + + if (image->current_frame == frame) + return Ok; + + if (!image->stream) + { + TRACE("image doesn't have an associated stream\n"); + return Ok; + } + + /* choose an appropriate image decoder */ + stat = get_decoder_info(image->stream, &codec); + if (stat != Ok) + { + WARN("can't find decoder info\n"); + return stat; + } + + /* seek to the start of the stream */ + seek.QuadPart = 0; + hr = IStream_Seek(image->stream, seek, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) + return hresult_to_status(hr); + + /* call on the image decoder to do the real work */ + stat = codec->decode_func(image->stream, &codec->info.Clsid, frame, &new_image); + + if (stat == Ok) + { + memcpy(&new_image->format, &codec->info.FormatID, sizeof(GUID)); + free_image_data(image); + if (image->type == ImageTypeBitmap) + *(GpBitmap *)image = *(GpBitmap *)new_image; + else if (image->type == ImageTypeMetafile) + *(GpMetafile *)image = *(GpMetafile *)new_image; + new_image->type = ~0; + GdipFree(new_image); + return Ok; + } + + return stat; +} + +GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream *stream, GpImage **image) { GpStatus stat; LARGE_INTEGER seek; @@ -2864,12 +3939,13 @@ GpStatus WINGDIPAPI GdipLoadImageFromStream(IStream* stream, GpImage **image) if (FAILED(hr)) return hresult_to_status(hr); /* call on the image decoder to do the real work */ - stat = codec->decode_func(stream, &codec->info.Clsid, image); + stat = codec->decode_func(stream, &codec->info.Clsid, 0, image); /* take note of the original data format */ if (stat == Ok) { memcpy(&(*image)->format, &codec->info.FormatID, sizeof(GUID)); + return Ok; } return stat; @@ -2902,12 +3978,14 @@ GpStatus WINGDIPAPI GdipSetPropertyItem(GpImage *image, GDIPCONST PropertyItem* { static int calls; - TRACE("(%p,%p)\n", image, item); + if (!image || !item) return InvalidParameter; + + TRACE("(%p,%p:%#x,%u,%u,%p)\n", image, item, item->id, item->type, item->length, item->value); if(!(calls++)) FIXME("not implemented\n"); - return NotImplemented; + return Ok; } GpStatus WINGDIPAPI GdipSaveImageToFile(GpImage *image, GDIPCONST WCHAR* filename, @@ -2997,35 +4075,26 @@ static GpStatus encode_image_WIC(GpImage *image, IStream* stream, hr = IWICBitmapFrameEncode_SetSize(frameencode, width, height); if (SUCCEEDED(hr)) - /* FIXME: use the resolution from the image */ - hr = IWICBitmapFrameEncode_SetResolution(frameencode, 96.0, 96.0); + hr = IWICBitmapFrameEncode_SetResolution(frameencode, image->xres, image->yres); if (SUCCEEDED(hr)) { - for (i=0; wic_pixel_formats[i]; i++) + for (i=0; pixel_formats[i].wic_format; i++) { - if (wic_gdip_formats[i] == bitmap->format) + if (pixel_formats[i].gdip_format == bitmap->format) + { + memcpy(&wicformat, pixel_formats[i].wic_format, sizeof(GUID)); + gdipformat = bitmap->format; break; + } } - if (wic_pixel_formats[i]) - memcpy(&wicformat, wic_pixel_formats[i], sizeof(GUID)); - else + if (!gdipformat) + { memcpy(&wicformat, &GUID_WICPixelFormat32bppBGRA, sizeof(GUID)); + gdipformat = PixelFormat32bppARGB; + } hr = IWICBitmapFrameEncode_SetPixelFormat(frameencode, &wicformat); - - for (i=0; wic_pixel_formats[i]; i++) - { - if (IsEqualGUID(&wicformat, wic_pixel_formats[i])) - break; - } - if (wic_pixel_formats[i]) - gdipformat = wic_gdip_formats[i]; - else - { - ERR("cannot provide pixel format %s\n", debugstr_guid(&wicformat)); - hr = E_FAIL; - } } if (SUCCEEDED(hr)) @@ -3124,26 +4193,46 @@ GpStatus WINGDIPAPI GdipSaveImageToStream(GpImage *image, IStream* stream, return stat; } +/***************************************************************************** + * GdipSaveAdd [GDIPLUS.@] + */ +GpStatus WINGDIPAPI GdipSaveAdd(GpImage *image, GDIPCONST EncoderParameters *params) +{ + FIXME("(%p,%p): stub\n", image, params); + return Ok; +} + /***************************************************************************** * GdipGetImagePalette [GDIPLUS.@] */ GpStatus WINGDIPAPI GdipGetImagePalette(GpImage *image, ColorPalette *palette, INT size) { + INT count; + TRACE("(%p,%p,%i)\n", image, palette, size); if (!image || !palette) return InvalidParameter; - if (size < (sizeof(UINT)*2+sizeof(ARGB)*image->palette_count)) + count = image->palette ? image->palette->Count : 0; + + if (size < (sizeof(UINT)*2+sizeof(ARGB)*count)) { TRACE("<-- InsufficientBuffer\n"); return InsufficientBuffer; } - palette->Flags = image->palette_flags; - palette->Count = image->palette_count; - memcpy(palette->Entries, image->palette_entries, sizeof(ARGB)*image->palette_count); - + if (image->palette) + { + palette->Flags = image->palette->Flags; + palette->Count = image->palette->Count; + memcpy(palette->Entries, image->palette->Entries, sizeof(ARGB)*image->palette->Count); + } + else + { + palette->Flags = 0; + palette->Count = 0; + } return Ok; } @@ -3153,26 +4242,21 @@ GpStatus WINGDIPAPI GdipGetImagePalette(GpImage *image, ColorPalette *palette, I GpStatus WINGDIPAPI GdipSetImagePalette(GpImage *image, GDIPCONST ColorPalette *palette) { + ColorPalette *new_palette; + TRACE("(%p,%p)\n", image, palette); if(!image || !palette || palette->Count > 256) return InvalidParameter; - if (palette->Count > image->palette_size) - { - ARGB *new_palette; + new_palette = GdipAlloc(2 * sizeof(UINT) + palette->Count * sizeof(ARGB)); + if (!new_palette) return OutOfMemory; - new_palette = GdipAlloc(sizeof(ARGB) * palette->Count); - if (!new_palette) return OutOfMemory; - - GdipFree(image->palette_entries); - image->palette_entries = new_palette; - image->palette_size = palette->Count; - } - - image->palette_flags = palette->Flags; - image->palette_count = palette->Count; - memcpy(image->palette_entries, palette->Entries, sizeof(ARGB)*palette->Count); + GdipFree(image->palette); + image->palette = new_palette; + image->palette->Flags = palette->Flags; + image->palette->Count = palette->Count; + memcpy(image->palette->Entries, palette->Entries, sizeof(ARGB)*palette->Count); return Ok; } @@ -3201,8 +4285,8 @@ static const WCHAR gif_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', static const WCHAR gif_extension[] = {'*','.','G','I','F',0}; static const WCHAR gif_mimetype[] = {'i','m','a','g','e','/','g','i','f', 0}; static const WCHAR gif_format[] = {'G','I','F',0}; -static const BYTE gif_sig_pattern[4] = "GIF8"; -static const BYTE gif_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF }; +static const BYTE gif_sig_pattern[12] = "GIF87aGIF89a"; +static const BYTE gif_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const WCHAR tiff_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'T','I','F','F', 0}; static const WCHAR tiff_extension[] = {'*','.','T','I','F','F',';','*','.','T','I','F',0}; @@ -3289,8 +4373,8 @@ static const struct image_codec codecs[NUM_CODECS] = { /* MimeType */ gif_mimetype, /* Flags */ ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin, /* Version */ 1, - /* SigCount */ 1, - /* SigSize */ 4, + /* SigCount */ 2, + /* SigSize */ 6, /* SigPattern */ gif_sig_pattern, /* SigMask */ gif_sig_mask, }, @@ -3561,7 +4645,6 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBi GpStatus retval; PixelFormat format; BitmapData lockeddata; - INT y; TRACE("%p %p %p\n", hbm, hpal, bitmap); @@ -3610,62 +4693,31 @@ GpStatus WINGDIPAPI GdipCreateBitmapFromHBITMAP(HBITMAP hbm, HPALETTE hpal, GpBi format, &lockeddata); if (retval == Ok) { - if (bm.bmBits) + HDC hdc; + BITMAPINFO *pbmi; + INT src_height; + + hdc = CreateCompatibleDC(NULL); + pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)); + + if (pbmi) { - for (y=0; ybmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmi->bmiHeader.biBitCount = 0; + + GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS); + + src_height = abs(pbmi->bmiHeader.biHeight); + pbmi->bmiHeader.biHeight = -src_height; + + GetDIBits(hdc, hbm, 0, src_height, lockeddata.Scan0, pbmi, DIB_RGB_COLORS); + + GdipFree(pbmi); } else - { - HDC hdc; - HBITMAP oldhbm; - BITMAPINFO *pbmi; - INT src_height, dst_stride; - BYTE *dst_bits; + retval = OutOfMemory; - hdc = CreateCompatibleDC(NULL); - oldhbm = SelectObject(hdc, hbm); - - pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)); - - if (pbmi) - { - pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - pbmi->bmiHeader.biBitCount = 0; - - GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS); - - src_height = abs(pbmi->bmiHeader.biHeight); - - if (pbmi->bmiHeader.biHeight > 0) - { - dst_bits = (BYTE*)lockeddata.Scan0+lockeddata.Stride*(src_height-1); - dst_stride = -lockeddata.Stride; - } - else - { - dst_bits = lockeddata.Scan0; - dst_stride = lockeddata.Stride; - } - - for (y=0; yimage.type = ImageTypeMetafile; (*metafile)->image.picture = NULL; (*metafile)->image.flags = ImageFlagsNone; - (*metafile)->image.palette_flags = 0; - (*metafile)->image.palette_count = 0; - (*metafile)->image.palette_size = 0; - (*metafile)->image.palette_entries = NULL; + (*metafile)->image.palette = NULL; (*metafile)->bounds = *frameRect; (*metafile)->unit = frameUnit; (*metafile)->metafile_type = type; @@ -551,3 +548,41 @@ GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics, return stat; } + +static int CALLBACK get_metafile_type_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR, + int nObj, LPARAM lpData) +{ + MetafileType *result = (MetafileType*)lpData; + + if (lpEMFR->iType == EMR_GDICOMMENT) + { + const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR; + + if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0) + { + const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4]; + + if (4 + sizeof(EmfPlusRecordHeader) <= comment->cbData && + header->Type == EmfPlusRecordTypeHeader) + { + if ((header->Flags & 1) == 1) + *result = MetafileTypeEmfPlusDual; + else + *result = MetafileTypeEmfPlusOnly; + } + } + else + *result = MetafileTypeEmf; + } + else + *result = MetafileTypeEmf; + + return FALSE; +} + +MetafileType METAFILE_GetEmfType(HENHMETAFILE hemf) +{ + MetafileType result = MetafileTypeInvalid; + EnumEnhMetaFile(NULL, hemf, get_metafile_type_proc, &result, NULL); + return result; +} diff --git a/reactos/dll/win32/gdiplus/region.c b/reactos/dll/win32/gdiplus/region.c index 485c3a3e165..f62b59941a1 100644 --- a/reactos/dll/win32/gdiplus/region.c +++ b/reactos/dll/win32/gdiplus/region.c @@ -151,15 +151,15 @@ static inline GpStatus clone_element(const region_element* element, { case RegionDataRect: (*element2)->elementdata.rect = element->elementdata.rect; - break; + return Ok; case RegionDataEmptyRect: case RegionDataInfiniteRect: - break; + return Ok; case RegionDataPath: (*element2)->elementdata.pathdata.pathheader = element->elementdata.pathdata.pathheader; stat = GdipClonePath(element->elementdata.pathdata.path, &(*element2)->elementdata.pathdata.path); - if (stat != Ok) goto clone_out; + if (stat == Ok) return Ok; break; default: (*element2)->elementdata.combine.left = NULL; @@ -167,16 +167,15 @@ static inline GpStatus clone_element(const region_element* element, stat = clone_element(element->elementdata.combine.left, &(*element2)->elementdata.combine.left); - if (stat != Ok) goto clone_out; - stat = clone_element(element->elementdata.combine.right, - &(*element2)->elementdata.combine.right); - if (stat != Ok) goto clone_out; + if (stat == Ok) + { + stat = clone_element(element->elementdata.combine.right, + &(*element2)->elementdata.combine.right); + if (stat == Ok) return Ok; + } break; } - return Ok; - -clone_out: delete_element(*element2); *element2 = NULL; return stat; @@ -254,20 +253,20 @@ GpStatus WINGDIPAPI GdipCombineRegionPath(GpRegion *region, GpPath *path, Combin } left = GdipAlloc(sizeof(region_element)); - if (!left) - goto out; - *left = region->node; + if (left) + { + *left = region->node; + stat = clone_element(&path_region->node, &right); + if (stat == Ok) + { + fuse_region(region, left, right, mode); + GdipDeleteRegion(path_region); + return Ok; + } + } + else + stat = OutOfMemory; - stat = clone_element(&path_region->node, &right); - if (stat != Ok) - goto out; - - fuse_region(region, left, right, mode); - - GdipDeleteRegion(path_region); - return Ok; - -out: GdipFree(left); GdipDeleteRegion(path_region); return stat; @@ -301,20 +300,20 @@ GpStatus WINGDIPAPI GdipCombineRegionRect(GpRegion *region, } left = GdipAlloc(sizeof(region_element)); - if (!left) - goto out; - memcpy(left, ®ion->node, sizeof(region_element)); + if (left) + { + memcpy(left, ®ion->node, sizeof(region_element)); + stat = clone_element(&rect_region->node, &right); + if (stat == Ok) + { + fuse_region(region, left, right, mode); + GdipDeleteRegion(rect_region); + return Ok; + } + } + else + stat = OutOfMemory; - stat = clone_element(&rect_region->node, &right); - if (stat != Ok) - goto out; - - fuse_region(region, left, right, mode); - - GdipDeleteRegion(rect_region); - return Ok; - -out: GdipFree(left); GdipDeleteRegion(rect_region); return stat; @@ -399,6 +398,8 @@ GpStatus WINGDIPAPI GdipCreateRegion(GpRegion **region) if(!*region) return OutOfMemory; + TRACE("=> %p\n", *region); + return init_region(*region, RegionDataInfiniteRect); } @@ -680,6 +681,7 @@ GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics, if(!hrgn){ rect->X = rect->Y = -(REAL)(1 << 22); rect->Width = rect->Height = (REAL)(1 << 23); + TRACE("%p => infinite\n", region); return Ok; } @@ -688,6 +690,7 @@ GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics, rect->Y = r.top; rect->Width = r.right - r.left; rect->Height = r.bottom - r.top; + TRACE("%p => %s\n", region, debugstr_rectf(rect)); } else status = GenericError; @@ -712,10 +715,10 @@ GpStatus WINGDIPAPI GdipGetRegionBoundsI(GpRegion *region, GpGraphics *graphics, status = GdipGetRegionBounds(region, graphics, &rectf); if(status == Ok){ - rect->X = roundr(rectf.X); - rect->Y = roundr(rectf.X); - rect->Width = roundr(rectf.Width); - rect->Height = roundr(rectf.Height); + rect->X = gdip_round(rectf.X); + rect->Y = gdip_round(rectf.X); + rect->Width = gdip_round(rectf.Width); + rect->Height = gdip_round(rectf.Height); } return status; @@ -1080,12 +1083,19 @@ GpStatus WINGDIPAPI GdipGetRegionHRgn(GpRegion *region, GpGraphics *graphics, HR GpStatus WINGDIPAPI GdipIsEmptyRegion(GpRegion *region, GpGraphics *graphics, BOOL *res) { + GpStatus status; + GpRectF rect; + TRACE("(%p, %p, %p)\n", region, graphics, res); if(!region || !graphics || !res) return InvalidParameter; - *res = (region->node.type == RegionDataEmptyRect); + status = GdipGetRegionBounds(region, graphics, &rect); + if (status != Ok) return status; + + *res = rect.Width == 0.0 && rect.Height == 0.0; + TRACE("=> %d\n", *res); return Ok; } @@ -1210,7 +1220,7 @@ GpStatus WINGDIPAPI GdipIsVisibleRegionPoint(GpRegion* region, REAL x, REAL y, G return Ok; } - *res = PtInRegion(hrgn, roundr(x), roundr(y)); + *res = PtInRegion(hrgn, gdip_round(x), gdip_round(y)); DeleteObject(hrgn); diff --git a/reactos/dll/win32/gdiplus/stringformat.c b/reactos/dll/win32/gdiplus/stringformat.c index cf6ea1f392a..7d6bcb0e3fb 100644 --- a/reactos/dll/win32/gdiplus/stringformat.c +++ b/reactos/dll/win32/gdiplus/stringformat.c @@ -50,6 +50,7 @@ GpStatus WINGDIPAPI GdipCreateStringFormat(INT attr, LANGID lang, (*format)->digitsub = StringDigitSubstituteUser; (*format)->character_ranges = NULL; (*format)->range_count = 0; + (*format)->generic_typographic = FALSE; /* tabstops */ (*format)->tabcount = 0; (*format)->firsttab = 0.0; @@ -386,6 +387,9 @@ GpStatus WINGDIPAPI GdipStringFormatGetGenericTypographic(GpStringFormat **forma (*format)->hkprefix = HotkeyPrefixNone; (*format)->align = StringAlignmentNear; (*format)->vertalign = StringAlignmentNear; + (*format)->generic_typographic = TRUE; + + TRACE("%p => %p\n", format, *format); return Ok; } diff --git a/reactos/include/psdk/gdiplusenums.h b/reactos/include/psdk/gdiplusenums.h index 69c81540ffc..d0fc11b307e 100644 --- a/reactos/include/psdk/gdiplusenums.h +++ b/reactos/include/psdk/gdiplusenums.h @@ -299,13 +299,6 @@ enum HotkeyPrefix HotkeyPrefixHide = 2 }; -enum PaletteFlags -{ - PaletteFlagsHasAlpha = 1, - PaletteFlagsGrayScale = 2, - PaletteFlagsHalftone = 4 -}; - enum ImageCodecFlags { ImageCodecFlagsEncoder = 1, diff --git a/reactos/include/psdk/gdiplusimaging.h b/reactos/include/psdk/gdiplusimaging.h index 7c199c3d5bd..93114d95c42 100644 --- a/reactos/include/psdk/gdiplusimaging.h +++ b/reactos/include/psdk/gdiplusimaging.h @@ -364,6 +364,9 @@ typedef struct PropertyItem #define PropertyTagFrameDelay 0x5100 #define PropertyTagLoopCount 0x5101 +#define PropertyTagGlobalPalette 0x5102 +#define PropertyTagIndexBackground 0x5103 +#define PropertyTagIndexTransparent 0x5104 #define PropertyTagPixelUnit 0x5110 #define PropertyTagPixelPerUnitX 0x5111 diff --git a/reactos/include/psdk/gdipluspixelformats.h b/reactos/include/psdk/gdipluspixelformats.h index d7382f141ff..9922b246613 100644 --- a/reactos/include/psdk/gdipluspixelformats.h +++ b/reactos/include/psdk/gdipluspixelformats.h @@ -46,7 +46,40 @@ typedef INT PixelFormat; #define PixelFormat48bppRGB (12 | (48 << 8) | PixelFormatExtended) #define PixelFormat64bppARGB (13 | (64 << 8) | PixelFormatAlpha | PixelFormatCanonical | PixelFormatExtended) #define PixelFormat64bppPARGB (14 | (64 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatExtended) -#define PixelFormatMax 15 +#define PixelFormat32bppCMYK (15 | (32 << 8)) +#define PixelFormatMax 16 + +static inline BOOL IsIndexedPixelFormat(PixelFormat format) +{ + return (format & PixelFormatIndexed) != 0; +} + +static inline BOOL IsAlphaPixelFormat(PixelFormat format) +{ + return (format & PixelFormatAlpha) != 0; +} + +static inline BOOL IsCanonicalPixelFormat(PixelFormat format) +{ + return (format & PixelFormatCanonical) != 0; +} + +static inline BOOL IsExtendedPixelFormat(PixelFormat format) +{ + return (format & PixelFormatExtended) != 0; +} + +static inline UINT GetPixelFormatSize(PixelFormat format) +{ + return (format >> 8) & 0xff; +} + +enum PaletteFlags +{ + PaletteFlagsHasAlpha = 1, + PaletteFlagsGrayScale = 2, + PaletteFlagsHalftone = 4 +}; #ifdef __cplusplus