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 <math.h> 21 22 #include "windef.h" 23 #include "winbase.h" 24 #include "winerror.h" 25 #include "wine/debug.h" 26 #include "wingdi.h" 27 28 #include "objbase.h" 29 30 #include "winreg.h" 31 #include "shlwapi.h" 32 33 #include "gdiplus.h" 34 #include "gdiplus_private.h" 35 36 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); 37 38 static const REAL mm_per_inch = 25.4; 39 static const REAL point_per_inch = 72.0; 40 41 static Status WINAPI NotificationHook(ULONG_PTR *token) 42 { 43 TRACE("%p\n", token); 44 if(!token) 45 return InvalidParameter; 46 47 return Ok; 48 } 49 50 static void WINAPI NotificationUnhook(ULONG_PTR token) 51 { 52 TRACE("%ld\n", token); 53 } 54 55 /***************************************************** 56 * DllMain 57 */ 58 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) 59 { 60 TRACE("(%p, %d, %p)\n", hinst, reason, reserved); 61 62 switch(reason) 63 { 64 case DLL_PROCESS_ATTACH: 65 DisableThreadLibraryCalls( hinst ); 66 init_generic_string_formats(); 67 break; 68 69 case DLL_PROCESS_DETACH: 70 if (reserved) break; 71 free_installed_fonts(); 72 free_generic_string_formats(); 73 break; 74 } 75 return TRUE; 76 } 77 78 /***************************************************** 79 * GdiplusStartup [GDIPLUS.@] 80 */ 81 Status WINAPI GdiplusStartup(ULONG_PTR *token, const struct GdiplusStartupInput *input, 82 struct GdiplusStartupOutput *output) 83 { 84 if(!token || !input) 85 return InvalidParameter; 86 87 TRACE("%p %p %p\n", token, input, output); 88 TRACE("GdiplusStartupInput %d %p %d %d\n", input->GdiplusVersion, 89 input->DebugEventCallback, input->SuppressBackgroundThread, 90 input->SuppressExternalCodecs); 91 92 if(input->GdiplusVersion < 1 || input->GdiplusVersion > 2) 93 return UnsupportedGdiplusVersion; 94 95 if(input->SuppressBackgroundThread){ 96 if(!output) 97 return InvalidParameter; 98 99 output->NotificationHook = NotificationHook; 100 output->NotificationUnhook = NotificationUnhook; 101 } 102 103 *token = 0xdeadbeef; 104 105 /* FIXME: DebugEventCallback ignored */ 106 107 return Ok; 108 } 109 110 GpStatus WINAPI GdiplusNotificationHook(ULONG_PTR *token) 111 { 112 FIXME("%p\n", token); 113 return NotificationHook(token); 114 } 115 116 void WINAPI GdiplusNotificationUnhook(ULONG_PTR token) 117 { 118 FIXME("%ld\n", token); 119 NotificationUnhook(token); 120 } 121 122 /***************************************************** 123 * GdiplusShutdown [GDIPLUS.@] 124 */ 125 ULONG WINAPI GdiplusShutdown_wrapper(ULONG_PTR token) 126 { 127 /* Notice the slightly different prototype from the official 128 * signature which forces us to use the _wrapper suffix. 129 */ 130 131 /* FIXME: no object tracking */ 132 133 /* "bricksntiles" expects a return value of 0, which native 134 * coincidentally gives. 135 */ 136 return 0; 137 } 138 139 /***************************************************** 140 * GdipAlloc [GDIPLUS.@] 141 */ 142 void* WINGDIPAPI GdipAlloc(SIZE_T size) 143 { 144 return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); 145 } 146 147 /***************************************************** 148 * GdipFree [GDIPLUS.@] 149 */ 150 void WINGDIPAPI GdipFree(void* ptr) 151 { 152 HeapFree(GetProcessHeap(), 0, ptr); 153 } 154 155 /* Calculates the bezier points needed to fill in the arc portion starting at 156 * angle start and ending at end. These two angles should be no more than 90 157 * degrees from each other. x1, y1, x2, y2 describes the bounding box (upper 158 * left and width and height). Angles must be in radians. write_first indicates 159 * that the first bezier point should be written out (usually this is false). 160 * pt is the array of GpPointFs that gets written to. 161 **/ 162 static void add_arc_part(GpPointF * pt, REAL x1, REAL y1, REAL x2, REAL y2, 163 REAL start, REAL end, BOOL write_first) 164 { 165 REAL center_x, center_y, rad_x, rad_y, cos_start, cos_end, 166 sin_start, sin_end, a, half; 167 INT i; 168 169 rad_x = x2 / 2.0; 170 rad_y = y2 / 2.0; 171 center_x = x1 + rad_x; 172 center_y = y1 + rad_y; 173 174 cos_start = cos(start); 175 cos_end = cos(end); 176 sin_start = sin(start); 177 sin_end = sin(end); 178 179 half = (end - start) / 2.0; 180 a = 4.0 / 3.0 * (1 - cos(half)) / sin(half); 181 182 if(write_first){ 183 pt[0].X = cos_start; 184 pt[0].Y = sin_start; 185 } 186 pt[1].X = cos_start - a * sin_start; 187 pt[1].Y = sin_start + a * cos_start; 188 189 pt[3].X = cos_end; 190 pt[3].Y = sin_end; 191 pt[2].X = cos_end + a * sin_end; 192 pt[2].Y = sin_end - a * cos_end; 193 194 /* expand the points back from the unit circle to the ellipse */ 195 for(i = (write_first ? 0 : 1); i < 4; i ++){ 196 pt[i].X = pt[i].X * rad_x + center_x; 197 pt[i].Y = pt[i].Y * rad_y + center_y; 198 } 199 } 200 201 /* We plot the curve as if it is on a circle then stretch the points. This 202 * adjusts the angles so that when we stretch the points they will end in the 203 * right place. This is only complicated because atan and atan2 do not behave 204 * conveniently. */ 205 static void unstretch_angle(REAL * angle, REAL rad_x, REAL rad_y) 206 { 207 REAL stretched; 208 INT revs_off; 209 210 *angle = deg2rad(*angle); 211 212 if(fabs(cos(*angle)) < 0.00001 || fabs(sin(*angle)) < 0.00001) 213 return; 214 215 stretched = gdiplus_atan2(sin(*angle) / fabs(rad_y), cos(*angle) / fabs(rad_x)); 216 revs_off = gdip_round(*angle / (2.0 * M_PI)) - gdip_round(stretched / (2.0 * M_PI)); 217 stretched += ((REAL)revs_off) * M_PI * 2.0; 218 *angle = stretched; 219 } 220 221 /* Stores the bezier points that correspond to the arc in points. If points is 222 * null, just return the number of points needed to represent the arc. */ 223 INT arc2polybezier(GpPointF * points, REAL x1, REAL y1, REAL x2, REAL y2, 224 REAL startAngle, REAL sweepAngle) 225 { 226 INT i; 227 REAL end_angle, start_angle, endAngle; 228 229 endAngle = startAngle + sweepAngle; 230 unstretch_angle(&startAngle, x2 / 2.0, y2 / 2.0); 231 unstretch_angle(&endAngle, x2 / 2.0, y2 / 2.0); 232 233 /* start_angle and end_angle are the iterative variables */ 234 start_angle = startAngle; 235 236 for(i = 0; i < MAX_ARC_PTS - 1; i += 3){ 237 /* check if we've overshot the end angle */ 238 if( sweepAngle > 0.0 ) 239 { 240 if (start_angle >= endAngle) break; 241 end_angle = min(start_angle + M_PI_2, endAngle); 242 } 243 else 244 { 245 if (start_angle <= endAngle) break; 246 end_angle = max(start_angle - M_PI_2, endAngle); 247 } 248 249 if (points) 250 add_arc_part(&points[i], x1, y1, x2, y2, start_angle, end_angle, i == 0); 251 252 start_angle += M_PI_2 * (sweepAngle < 0.0 ? -1.0 : 1.0); 253 } 254 255 if (i == 0) return 0; 256 else return i+1; 257 } 258 259 COLORREF ARGB2COLORREF(ARGB color) 260 { 261 /* 262 Packing of these color structures: 263 COLORREF: 00bbggrr 264 ARGB: aarrggbb 265 FIXME:doesn't handle alpha channel 266 */ 267 return ((color & 0x0000ff) << 16) + 268 (color & 0x00ff00) + 269 ((color & 0xff0000) >> 16); 270 } 271 272 HBITMAP ARGB2BMP(ARGB color) 273 { 274 BITMAPINFO bi; 275 HBITMAP result; 276 RGBQUAD *bits; 277 int alpha; 278 279 if ((color & 0xff000000) == 0xff000000) return 0; 280 281 bi.bmiHeader.biSize = sizeof(bi.bmiHeader); 282 bi.bmiHeader.biWidth = 1; 283 bi.bmiHeader.biHeight = 1; 284 bi.bmiHeader.biPlanes = 1; 285 bi.bmiHeader.biBitCount = 32; 286 bi.bmiHeader.biCompression = BI_RGB; 287 bi.bmiHeader.biSizeImage = 0; 288 bi.bmiHeader.biXPelsPerMeter = 0; 289 bi.bmiHeader.biYPelsPerMeter = 0; 290 bi.bmiHeader.biClrUsed = 0; 291 bi.bmiHeader.biClrImportant = 0; 292 293 result = CreateDIBSection(0, &bi, DIB_RGB_COLORS, (void*)&bits, NULL, 0); 294 295 bits[0].rgbReserved = alpha = (color>>24)&0xff; 296 bits[0].rgbRed = ((color>>16)&0xff)*alpha/255; 297 bits[0].rgbGreen = ((color>>8)&0xff)*alpha/255; 298 bits[0].rgbBlue = (color&0xff)*alpha/255; 299 300 return result; 301 } 302 303 /* Like atan2, but puts angle in correct quadrant if dx is 0. */ 304 REAL gdiplus_atan2(REAL dy, REAL dx) 305 { 306 if((dx == 0.0) && (dy != 0.0)) 307 return dy > 0.0 ? M_PI_2 : -M_PI_2; 308 309 return atan2(dy, dx); 310 } 311 312 GpStatus hresult_to_status(HRESULT res) 313 { 314 switch(res){ 315 case S_OK: 316 return Ok; 317 case E_OUTOFMEMORY: 318 return OutOfMemory; 319 case E_INVALIDARG: 320 return InvalidParameter; 321 default: 322 return GenericError; 323 } 324 } 325 326 /* converts a given unit to its value in pixels */ 327 REAL units_to_pixels(REAL units, GpUnit unit, REAL dpi) 328 { 329 switch (unit) 330 { 331 case UnitPixel: 332 case UnitWorld: 333 case UnitDisplay: 334 return units; 335 case UnitPoint: 336 return units * dpi / point_per_inch; 337 case UnitInch: 338 return units * dpi; 339 case UnitDocument: 340 return units * dpi / 300.0; /* Per MSDN */ 341 case UnitMillimeter: 342 return units * dpi / mm_per_inch; 343 default: 344 FIXME("Unhandled unit type: %d\n", unit); 345 return 0; 346 } 347 } 348 349 /* converts value in pixels to a given unit */ 350 REAL pixels_to_units(REAL pixels, GpUnit unit, REAL dpi) 351 { 352 switch (unit) 353 { 354 case UnitPixel: 355 case UnitWorld: 356 case UnitDisplay: 357 return pixels; 358 case UnitPoint: 359 return pixels * point_per_inch / dpi; 360 case UnitInch: 361 return pixels / dpi; 362 case UnitDocument: 363 return pixels * 300.0 / dpi; 364 case UnitMillimeter: 365 return pixels * mm_per_inch / dpi; 366 default: 367 FIXME("Unhandled unit type: %d\n", unit); 368 return 0; 369 } 370 } 371 372 REAL units_scale(GpUnit from, GpUnit to, REAL dpi) 373 { 374 REAL pixels = units_to_pixels(1.0, from, dpi); 375 return pixels_to_units(pixels, to, dpi); 376 } 377 378 /* Calculates Bezier points from cardinal spline points. */ 379 void calc_curve_bezier(const GpPointF *pts, REAL tension, REAL *x1, 380 REAL *y1, REAL *x2, REAL *y2) 381 { 382 REAL xdiff, ydiff; 383 384 /* calculate tangent */ 385 xdiff = pts[2].X - pts[0].X; 386 ydiff = pts[2].Y - pts[0].Y; 387 388 /* apply tangent to get control points */ 389 *x1 = pts[1].X - tension * xdiff; 390 *y1 = pts[1].Y - tension * ydiff; 391 *x2 = pts[1].X + tension * xdiff; 392 *y2 = pts[1].Y + tension * ydiff; 393 } 394 395 /* Calculates Bezier points from cardinal spline endpoints. */ 396 void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj, 397 REAL tension, REAL *x, REAL *y) 398 { 399 /* tangent at endpoints is the line from the endpoint to the adjacent point */ 400 *x = gdip_round(tension * (xadj - xend) + xend); 401 *y = gdip_round(tension * (yadj - yend) + yend); 402 } 403 404 /* make sure path has enough space for len more points */ 405 BOOL lengthen_path(GpPath *path, INT len) 406 { 407 /* initial allocation */ 408 if(path->datalen == 0){ 409 path->datalen = len * 2; 410 411 path->pathdata.Points = heap_alloc_zero(path->datalen * sizeof(PointF)); 412 if(!path->pathdata.Points) return FALSE; 413 414 path->pathdata.Types = heap_alloc_zero(path->datalen); 415 if(!path->pathdata.Types){ 416 heap_free(path->pathdata.Points); 417 return FALSE; 418 } 419 } 420 /* reallocation, double size of arrays */ 421 else if(path->datalen - path->pathdata.Count < len){ 422 while(path->datalen - path->pathdata.Count < len) 423 path->datalen *= 2; 424 425 path->pathdata.Points = heap_realloc(path->pathdata.Points, path->datalen * sizeof(PointF)); 426 if(!path->pathdata.Points) return FALSE; 427 428 path->pathdata.Types = heap_realloc(path->pathdata.Types, path->datalen); 429 if(!path->pathdata.Types) return FALSE; 430 } 431 432 return TRUE; 433 } 434 435 void convert_32bppARGB_to_32bppPARGB(UINT width, UINT height, 436 BYTE *dst_bits, INT dst_stride, const BYTE *src_bits, INT src_stride) 437 { 438 INT x, y; 439 for (y=0; y<height; y++) 440 { 441 const BYTE *src=src_bits+y*src_stride; 442 BYTE *dst=dst_bits+y*dst_stride; 443 for (x=0; x<width; x++) 444 { 445 BYTE alpha=src[3]; 446 *dst++ = (*src++ * alpha + 127) / 255; 447 *dst++ = (*src++ * alpha + 127) / 255; 448 *dst++ = (*src++ * alpha + 127) / 255; 449 *dst++ = *src++; 450 } 451 } 452 } 453 454 /* recursive deletion of GpRegion nodes */ 455 void delete_element(region_element* element) 456 { 457 switch(element->type) 458 { 459 case RegionDataRect: 460 break; 461 case RegionDataPath: 462 GdipDeletePath(element->elementdata.path); 463 break; 464 case RegionDataEmptyRect: 465 case RegionDataInfiniteRect: 466 break; 467 default: 468 delete_element(element->elementdata.combine.left); 469 delete_element(element->elementdata.combine.right); 470 heap_free(element->elementdata.combine.left); 471 heap_free(element->elementdata.combine.right); 472 break; 473 } 474 } 475 476 const char *debugstr_rectf(const RectF* rc) 477 { 478 if (!rc) return "(null)"; 479 return wine_dbg_sprintf("(%0.2f,%0.2f,%0.2f,%0.2f)", rc->X, rc->Y, rc->Width, rc->Height); 480 } 481 482 const char *debugstr_pointf(const PointF* pt) 483 { 484 if (!pt) return "(null)"; 485 return wine_dbg_sprintf("(%0.2f,%0.2f)", pt->X, pt->Y); 486 } 487