1 /*
2 * Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com)
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5 * and associated documentation files (the "Software"), to deal in the Software without restriction,
6 * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
8 * subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in all copies or substantial
11 * portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
14 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
15 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
16 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
17 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 *
19 * Authors:
20 * Sebastien Pouliot <sebastien@ximian.com>
21 */
22
23 #include "region-path-tree.h"
24 #include "graphics-path-private.h"
25
26 /*
27 * gdip_region_clear_tree:
28 * @tree: a GpPathTree to clear
29 *
30 * Recursively clear (delete path and free memory) the specified path tree.
31 */
32 void
gdip_region_clear_tree(GpPathTree * tree)33 gdip_region_clear_tree (GpPathTree *tree)
34 {
35 if (!tree)
36 return;
37
38 if (tree->path) {
39 GdipDeletePath (tree->path);
40 tree->path = NULL;
41 } else {
42 gdip_region_clear_tree (tree->branch1);
43 GdipFree (tree->branch1);
44 gdip_region_clear_tree (tree->branch2);
45 GdipFree (tree->branch2);
46 }
47 }
48
49
50 /*
51 * gdip_region_copy_tree:
52 * @source: the GpPathTree to copy
53 * @dest: the GpPathTree copy
54 *
55 * Recursively copy (and allocate) the @source path tree into @dest.
56 * If @source is present then we must have a valid @dest.
57 */
58 GpStatus
gdip_region_copy_tree(GpPathTree * source,GpPathTree * dest)59 gdip_region_copy_tree (GpPathTree *source, GpPathTree *dest)
60 {
61 GpStatus status;
62
63 if (!source)
64 return Ok;
65
66 g_assert (dest);
67 if (source->path) {
68 status = GdipClonePath (source->path, &dest->path);
69 if (status != Ok)
70 return status;
71
72 dest->branch1 = NULL;
73 dest->branch2 = NULL;
74 } else {
75 dest->path = NULL;
76 dest->mode = source->mode;
77 dest->branch1 = (GpPathTree *) GdipAlloc (sizeof (GpPathTree));
78 if (!dest->branch1)
79 return OutOfMemory;
80
81 status = gdip_region_copy_tree (source->branch1, dest->branch1);
82 if (status != Ok)
83 return status;
84
85 dest->branch2 = (GpPathTree *) GdipAlloc (sizeof (GpPathTree));
86 if (!dest->branch2)
87 return OutOfMemory;
88
89 status = gdip_region_copy_tree (source->branch2, dest->branch2);
90 if (status != Ok)
91 return status;
92 }
93
94 return Ok;
95 }
96
97
98 /*
99 * gdip_region_get_tree_size:
100 * @tree: a GpPathTree
101 *
102 * Recursively calculate the size (in bytes) required to serialized @tree.
103 */
104 UINT
gdip_region_get_tree_size(GpPathTree * tree)105 gdip_region_get_tree_size (GpPathTree *tree)
106 {
107 UINT result;
108
109 if (tree->path) {
110 /* tag, count, fillmode, types and points */
111 result = 3 * sizeof (UINT) +
112 (tree->path->count * sizeof (BYTE)) +
113 (tree->path->count * sizeof (GpPointF));
114 } else {
115 /* tag, operation, size (branch1), branch1, size (branch2), branch2 */
116 result = 4 * sizeof (guint32);
117 result += gdip_region_get_tree_size (tree->branch1);
118 result += gdip_region_get_tree_size (tree->branch2);
119 }
120 return result;
121 }
122
123
124 /*
125 * gdip_region_deserialize_tree:
126 * @data: a byte array
127 * @size: the length of the byte array
128 * @tree: a GpPathTree
129 *
130 * Recursively deserialize the @tree from the supplied buffer @data. Returns
131 * TRUE if the deserialization was possible, or FALSE if a problem was found
132 * (e.g. @size mismatch, bad data...)
133 */
134 BOOL
gdip_region_deserialize_tree(BYTE * data,int size,GpPathTree * tree)135 gdip_region_deserialize_tree (BYTE *data, int size, GpPathTree *tree)
136 {
137 int len = sizeof (guint32);
138 guint32 tag;
139
140 memcpy (&tag, data, len);
141 data += len;
142 size -= len;
143
144 switch (tag) {
145 case REGION_TAG_PATH: {
146 /* deserialize a path from the memory blob */
147 guint32 count;
148 FillMode mode;
149
150 tree->mode = CombineModeReplace;
151 tree->branch1 = NULL;
152 tree->branch2 = NULL;
153 /* count */
154 memcpy (&count, data, len);
155 data += len;
156 size -= len;
157 /* mode (FillMode, not CombineMode) */
158 memcpy (&mode, data, len);
159 data += len;
160 size -= len;
161 /* check that the size match the length of the type (byte) and
162 GpPointF for the specified count */
163 if (size == count + count * sizeof (GpPointF)) {
164 BYTE* types = data;
165 GpPointF *points = (GpPointF*) (data + count);
166 return (GdipCreatePath2 (points, types, count, mode, &tree->path) == Ok);
167 }
168 return FALSE;
169 }
170 break;
171 case REGION_TAG_TREE: {
172 guint branch_size;
173 tree->path = NULL;
174 /* operation */
175 memcpy (&tree->mode, data, len);
176 data += len;
177 size -= len;
178 /* size (branch1) */
179 memcpy (&branch_size, data, len);
180 data += len;
181 size -= len;
182 /* deserialize a tree from the memory blob */
183 tree->branch1 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
184 if (!tree->branch1)
185 return FALSE;
186
187 if (!gdip_region_deserialize_tree (data, branch_size, tree->branch1))
188 return FALSE;
189 data += branch_size;
190 size -= branch_size;
191 /* size (branch2) */
192 memcpy (&branch_size, data, len);
193 data += len;
194 size -= len;
195 tree->branch2 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
196 if (!tree->branch2)
197 return FALSE;
198
199 if (!gdip_region_deserialize_tree (data, branch_size, tree->branch2))
200 return FALSE;
201 }
202 break;
203 default:
204 g_warning ("Invalid tag %d", tag);
205 return FALSE;
206 }
207 return TRUE;
208 }
209
210
211 /*
212 * gdip_region_serialize_tree:
213 * @tree: a GpPathTree
214 * @buffer: a byte array
215 * @bufferSize: the length of the byte array
216 * @sizeFilled: a pointer to a integer
217 *
218 * Recursively serialize the @tree data in the supplied @buffer. Returns TRUE
219 * if the serialization was possible, or FALSE if a problem was found (e.g.
220 * @bufferSize too small). If successful @sizeFilled will contains the actual
221 * number of bytes that were required to serialize @tree.
222 */
223 BOOL
gdip_region_serialize_tree(GpPathTree * tree,BYTE * buffer,UINT bufferSize,UINT * sizeFilled)224 gdip_region_serialize_tree (GpPathTree *tree, BYTE *buffer, UINT bufferSize, UINT *sizeFilled)
225 {
226 if (tree->path) {
227 /* tag */
228 guint32 temp = REGION_TAG_PATH;
229 int len = sizeof (guint32);
230 memcpy (buffer, &temp, len);
231 buffer += len;
232 *sizeFilled += len;
233 /* count */
234 memcpy (buffer, &tree->path->count, len);
235 buffer += len;
236 *sizeFilled += len;
237 /* fill_mode */
238 temp = tree->path->fill_mode;
239 memcpy (buffer, &temp, len);
240 buffer += len;
241 *sizeFilled += len;
242 /* types */
243 len = tree->path->count;
244 memcpy (buffer, tree->path->types, len);
245 buffer += len;
246 *sizeFilled += len;
247 /* points */
248 len = tree->path->count * sizeof (GpPointF);
249 memcpy (buffer, tree->path->points, len);
250 buffer += len;
251 *sizeFilled += len;
252 } else {
253 /* tag */
254 BYTE *original = buffer;
255 guint32 temp = REGION_TAG_TREE;
256 int len = sizeof (guint32);
257 memcpy (buffer, &temp, len);
258 buffer += len;
259 *sizeFilled += len;
260 /* operation */
261 temp = tree->mode;
262 memcpy (buffer, &temp, len);
263 buffer += len;
264 *sizeFilled += len;
265 /* serialize branch 1 (size + branch) */
266 temp = gdip_region_get_tree_size (tree->branch1);
267 memcpy (buffer, &temp, len);
268 buffer += len;
269 *sizeFilled += len;
270 if (!gdip_region_serialize_tree (tree->branch1, buffer, bufferSize - (buffer - original), sizeFilled))
271 return FALSE;
272 buffer += temp;
273 /* serialize branch 2 (size + branch) */
274 temp = gdip_region_get_tree_size (tree->branch2);
275 memcpy (buffer, &temp, len);
276 buffer += len;
277 *sizeFilled += len;
278 if (!gdip_region_serialize_tree (tree->branch2, buffer, bufferSize - (buffer - original), sizeFilled))
279 return FALSE;
280 }
281 return TRUE;
282 }
283
284
285 /*
286 * gdip_region_transform_tree:
287 * @tree: a GpPathTree
288 * @matrix: the GpMatrix to apply to the tree
289 *
290 * Recursively apply the @matrix to the @tree.
291 */
292 GpStatus
gdip_region_transform_tree(GpPathTree * tree,GpMatrix * matrix)293 gdip_region_transform_tree (GpPathTree *tree, GpMatrix *matrix)
294 {
295 if (tree->path) {
296 return GdipTransformPath (tree->path, matrix);
297 } else {
298 GpStatus status;
299 status = gdip_region_transform_tree (tree->branch1, matrix);
300 if (status == Ok)
301 status = gdip_region_transform_tree (tree->branch2, matrix);
302 return status;
303 }
304 }
305
306
307 /*
308 * gdip_region_translate_tree:
309 * @tree: a GpPathTree
310 * @dx: the delta x to apply to each point
311 * @dy: the delta y to apply to each point
312 *
313 * Recursively apply the @dx, @dy translation to each point, of each path,
314 * in the @tree.
315 */
316 void
gdip_region_translate_tree(GpPathTree * tree,float dx,float dy)317 gdip_region_translate_tree (GpPathTree *tree, float dx, float dy)
318 {
319 if (tree->path) {
320 int i;
321 for (i = 0; i < tree->path->count; i++) {
322 GpPointF *point = tree->path->points + i;
323 point->X += dx;
324 point->Y += dy;
325 }
326 } else {
327 gdip_region_translate_tree (tree->branch1, dx, dy);
328 gdip_region_translate_tree (tree->branch2, dx, dy);
329 }
330 }
331