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