1 /* 2 * Copyright (C) 2007 Google (Evan Stade) 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #include <stdarg.h> 20 #include <assert.h> 21 22 #include "windef.h" 23 #include "winbase.h" 24 #include "wingdi.h" 25 26 #include "objbase.h" 27 28 #include "gdiplus.h" 29 #include "gdiplus_private.h" 30 #include "wine/debug.h" 31 32 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); 33 34 GpStatus WINGDIPAPI GdipCloneCustomLineCap(GpCustomLineCap* from, 35 GpCustomLineCap** to) 36 { 37 TRACE("(%p, %p)\n", from, to); 38 39 if(!from || !to) 40 return InvalidParameter; 41 42 if (from->type == CustomLineCapTypeDefault) 43 *to = heap_alloc_zero(sizeof(GpCustomLineCap)); 44 else 45 *to = heap_alloc_zero(sizeof(GpAdjustableArrowCap)); 46 47 if (!*to) 48 return OutOfMemory; 49 50 if (from->type == CustomLineCapTypeDefault) 51 **to = *from; 52 else 53 *(GpAdjustableArrowCap *)*to = *(GpAdjustableArrowCap *)from; 54 55 /* Duplicate path data */ 56 (*to)->pathdata.Points = heap_alloc_zero(from->pathdata.Count * sizeof(PointF)); 57 (*to)->pathdata.Types = heap_alloc_zero(from->pathdata.Count); 58 59 if((!(*to)->pathdata.Types || !(*to)->pathdata.Points) && (*to)->pathdata.Count){ 60 heap_free((*to)->pathdata.Points); 61 heap_free((*to)->pathdata.Types); 62 heap_free(*to); 63 return OutOfMemory; 64 } 65 66 memcpy((*to)->pathdata.Points, from->pathdata.Points, from->pathdata.Count 67 * sizeof(PointF)); 68 memcpy((*to)->pathdata.Types, from->pathdata.Types, from->pathdata.Count); 69 70 TRACE("<-- %p\n", *to); 71 72 return Ok; 73 } 74 75 static GpStatus init_custom_linecap(GpCustomLineCap *cap, GpPathData *pathdata, BOOL fill, GpLineCap basecap, 76 REAL base_inset) 77 { 78 cap->fill = fill; 79 80 cap->pathdata.Points = heap_alloc_zero(pathdata->Count * sizeof(PointF)); 81 cap->pathdata.Types = heap_alloc_zero(pathdata->Count); 82 83 if ((!cap->pathdata.Types || !cap->pathdata.Points) && pathdata->Count) 84 { 85 heap_free(cap->pathdata.Points); 86 heap_free(cap->pathdata.Types); 87 cap->pathdata.Points = NULL; 88 cap->pathdata.Types = NULL; 89 return OutOfMemory; 90 } 91 92 if (pathdata->Points) 93 memcpy(cap->pathdata.Points, pathdata->Points, pathdata->Count * sizeof(PointF)); 94 if (pathdata->Types) 95 memcpy(cap->pathdata.Types, pathdata->Types, pathdata->Count); 96 cap->pathdata.Count = pathdata->Count; 97 98 cap->inset = base_inset; 99 cap->cap = basecap; 100 cap->join = LineJoinMiter; 101 cap->scale = 1.0; 102 103 return Ok; 104 } 105 106 /* FIXME: Sometimes when fillPath is non-null and stroke path is null, the native 107 * version of this function returns NotImplemented. I cannot figure out why. */ 108 GpStatus WINGDIPAPI GdipCreateCustomLineCap(GpPath* fillPath, GpPath* strokePath, 109 GpLineCap baseCap, REAL baseInset, GpCustomLineCap **customCap) 110 { 111 GpPathData *pathdata; 112 GpStatus stat; 113 114 TRACE("%p %p %d %f %p\n", fillPath, strokePath, baseCap, baseInset, customCap); 115 116 if(!customCap || !(fillPath || strokePath)) 117 return InvalidParameter; 118 119 *customCap = heap_alloc_zero(sizeof(GpCustomLineCap)); 120 if(!*customCap) return OutOfMemory; 121 122 if (strokePath) 123 pathdata = &strokePath->pathdata; 124 else 125 pathdata = &fillPath->pathdata; 126 127 stat = init_custom_linecap(*customCap, pathdata, fillPath != NULL, baseCap, baseInset); 128 if (stat != Ok) 129 { 130 heap_free(*customCap); 131 return stat; 132 } 133 134 TRACE("<-- %p\n", *customCap); 135 136 return Ok; 137 } 138 139 GpStatus WINGDIPAPI GdipDeleteCustomLineCap(GpCustomLineCap *customCap) 140 { 141 TRACE("(%p)\n", customCap); 142 143 if(!customCap) 144 return InvalidParameter; 145 146 heap_free(customCap->pathdata.Points); 147 heap_free(customCap->pathdata.Types); 148 heap_free(customCap); 149 150 return Ok; 151 } 152 153 GpStatus WINGDIPAPI GdipGetCustomLineCapStrokeJoin(GpCustomLineCap* customCap, 154 GpLineJoin* lineJoin) 155 { 156 TRACE("(%p, %p)\n", customCap, lineJoin); 157 158 if(!customCap || !lineJoin) 159 return InvalidParameter; 160 161 *lineJoin = customCap->join; 162 163 return Ok; 164 } 165 166 GpStatus WINGDIPAPI GdipGetCustomLineCapWidthScale(GpCustomLineCap* custom, 167 REAL* widthScale) 168 { 169 TRACE("(%p, %p)\n", custom, widthScale); 170 171 if(!custom || !widthScale) 172 return InvalidParameter; 173 174 *widthScale = custom->scale; 175 176 return Ok; 177 } 178 179 GpStatus WINGDIPAPI GdipSetCustomLineCapStrokeCaps(GpCustomLineCap* custom, 180 GpLineCap start, GpLineCap end) 181 { 182 static int calls; 183 184 TRACE("(%p,%u,%u)\n", custom, start, end); 185 186 if(!custom) 187 return InvalidParameter; 188 189 if(!(calls++)) 190 FIXME("not implemented\n"); 191 192 return NotImplemented; 193 } 194 195 GpStatus WINGDIPAPI GdipSetCustomLineCapBaseCap(GpCustomLineCap* custom, 196 GpLineCap base) 197 { 198 static int calls; 199 200 TRACE("(%p,%u)\n", custom, base); 201 202 if(!(calls++)) 203 FIXME("not implemented\n"); 204 205 return NotImplemented; 206 } 207 208 GpStatus WINGDIPAPI GdipGetCustomLineCapBaseInset(GpCustomLineCap* custom, 209 REAL* inset) 210 { 211 TRACE("(%p, %p)\n", custom, inset); 212 213 if(!custom || !inset) 214 return InvalidParameter; 215 216 *inset = custom->inset; 217 218 return Ok; 219 } 220 221 GpStatus WINGDIPAPI GdipSetCustomLineCapBaseInset(GpCustomLineCap* custom, 222 REAL inset) 223 { 224 static int calls; 225 226 TRACE("(%p,%0.2f)\n", custom, inset); 227 228 if(!(calls++)) 229 FIXME("not implemented\n"); 230 231 return NotImplemented; 232 } 233 234 /*FIXME: LineJoin completely ignored now */ 235 GpStatus WINGDIPAPI GdipSetCustomLineCapStrokeJoin(GpCustomLineCap* custom, 236 GpLineJoin join) 237 { 238 TRACE("(%p, %d)\n", custom, join); 239 240 if(!custom) 241 return InvalidParameter; 242 243 custom->join = join; 244 245 return Ok; 246 } 247 248 GpStatus WINGDIPAPI GdipSetCustomLineCapWidthScale(GpCustomLineCap* custom, REAL width) 249 { 250 TRACE("(%p,%0.2f)\n", custom, width); 251 252 if(!custom) 253 return InvalidParameter; 254 255 custom->scale = width; 256 257 return Ok; 258 } 259 260 GpStatus WINGDIPAPI GdipGetCustomLineCapBaseCap(GpCustomLineCap *customCap, GpLineCap *baseCap) 261 { 262 TRACE("(%p, %p)\n", customCap, baseCap); 263 264 if(!customCap || !baseCap) 265 return InvalidParameter; 266 267 *baseCap = customCap->cap; 268 269 return Ok; 270 } 271 272 GpStatus WINGDIPAPI GdipGetCustomLineCapType(GpCustomLineCap *customCap, CustomLineCapType *type) 273 { 274 TRACE("(%p, %p)\n", customCap, type); 275 276 if(!customCap || !type) 277 return InvalidParameter; 278 279 *type = customCap->type; 280 return Ok; 281 } 282 283 static void arrowcap_update_path(GpAdjustableArrowCap *cap) 284 { 285 static const BYTE types_filled[] = 286 { 287 PathPointTypeStart, PathPointTypeLine, PathPointTypeLine, PathPointTypeLine | PathPointTypeCloseSubpath 288 }; 289 static const BYTE types_unfilled[] = 290 { 291 PathPointTypeStart, PathPointTypeLine, PathPointTypeLine 292 }; 293 GpPointF *points; 294 295 assert(cap->cap.pathdata.Count == 3 || cap->cap.pathdata.Count == 4); 296 297 points = cap->cap.pathdata.Points; 298 if (cap->cap.fill) 299 { 300 memcpy(cap->cap.pathdata.Types, types_filled, sizeof(types_filled)); 301 cap->cap.pathdata.Count = 4; 302 points[0].X = -cap->width / 2.0; 303 points[0].Y = -cap->height; 304 points[1].X = 0.0; 305 points[1].Y = 0.0; 306 points[2].X = cap->width / 2.0; 307 points[2].Y = -cap->height; 308 points[3].X = 0.0; 309 points[3].Y = -cap->height - cap->middle_inset; 310 } 311 else 312 { 313 memcpy(cap->cap.pathdata.Types, types_unfilled, sizeof(types_unfilled)); 314 cap->cap.pathdata.Count = 3; 315 points[0].X = -cap->width / 4.0; 316 points[0].Y = -cap->height / 2.0; 317 points[1].X = 0.0; 318 points[1].Y = 0.0; 319 points[2].X = cap->width / 4.0; 320 points[2].Y = -cap->height / 2.0; 321 } 322 323 if (cap->width == 0.0) 324 cap->cap.inset = 0.0; 325 else 326 cap->cap.inset = cap->height / cap->width; 327 } 328 329 GpStatus WINGDIPAPI GdipCreateAdjustableArrowCap(REAL height, REAL width, BOOL fill, 330 GpAdjustableArrowCap **cap) 331 { 332 GpPathData pathdata; 333 GpStatus stat; 334 335 TRACE("(%0.2f,%0.2f,%i,%p)\n", height, width, fill, cap); 336 337 if (!cap) 338 return InvalidParameter; 339 340 *cap = heap_alloc_zero(sizeof(**cap)); 341 if (!*cap) 342 return OutOfMemory; 343 344 /* We'll need 4 points at most. */ 345 pathdata.Count = 4; 346 pathdata.Points = NULL; 347 pathdata.Types = NULL; 348 stat = init_custom_linecap(&(*cap)->cap, &pathdata, fill, LineCapTriangle, width != 0.0 ? height / width : 0.0); 349 if (stat != Ok) 350 { 351 heap_free(*cap); 352 return stat; 353 } 354 355 (*cap)->cap.type = CustomLineCapTypeAdjustableArrow; 356 (*cap)->height = height; 357 (*cap)->width = width; 358 (*cap)->middle_inset = 0.0; 359 arrowcap_update_path(*cap); 360 361 return Ok; 362 } 363 364 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapFillState(GpAdjustableArrowCap* cap, BOOL* fill) 365 { 366 TRACE("(%p,%p)\n", cap, fill); 367 368 if (!cap || !fill) 369 return InvalidParameter; 370 371 *fill = cap->cap.fill; 372 return Ok; 373 } 374 375 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapHeight(GpAdjustableArrowCap* cap, REAL* height) 376 { 377 TRACE("(%p,%p)\n", cap, height); 378 379 if (!cap || !height) 380 return InvalidParameter; 381 382 *height = cap->height; 383 return Ok; 384 } 385 386 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapMiddleInset(GpAdjustableArrowCap* cap, REAL* middle) 387 { 388 TRACE("(%p,%p)\n", cap, middle); 389 390 if (!cap || !middle) 391 return InvalidParameter; 392 393 *middle = cap->middle_inset; 394 return Ok; 395 } 396 397 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapWidth(GpAdjustableArrowCap* cap, REAL* width) 398 { 399 TRACE("(%p,%p)\n", cap, width); 400 401 if (!cap || !width) 402 return InvalidParameter; 403 404 *width = cap->width; 405 return Ok; 406 } 407 408 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapFillState(GpAdjustableArrowCap* cap, BOOL fill) 409 { 410 TRACE("(%p,%i)\n", cap, fill); 411 412 if (!cap) 413 return InvalidParameter; 414 415 cap->cap.fill = fill; 416 arrowcap_update_path(cap); 417 return Ok; 418 } 419 420 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapHeight(GpAdjustableArrowCap* cap, REAL height) 421 { 422 TRACE("(%p,%0.2f)\n", cap, height); 423 424 if (!cap) 425 return InvalidParameter; 426 427 cap->height = height; 428 arrowcap_update_path(cap); 429 return Ok; 430 } 431 432 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapMiddleInset(GpAdjustableArrowCap* cap, REAL middle) 433 { 434 TRACE("(%p,%0.2f)\n", cap, middle); 435 436 if (!cap) 437 return InvalidParameter; 438 439 cap->middle_inset = middle; 440 arrowcap_update_path(cap); 441 return Ok; 442 } 443 444 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapWidth(GpAdjustableArrowCap* cap, REAL width) 445 { 446 TRACE("(%p,%0.2f)\n", cap, width); 447 448 if (!cap) 449 return InvalidParameter; 450 451 cap->width = width; 452 arrowcap_update_path(cap); 453 return Ok; 454 } 455