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