gdiplus: Use ARRAY_SIZE() macro.
[wine.git] / dlls / gdiplus / gdiplus_private.h
blob9fff578a28080cbd0566790e908e3834c39399fb
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
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.
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.
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
19 #ifndef __WINE_GP_PRIVATE_H_
20 #define __WINE_GP_PRIVATE_H_
22 #include <math.h>
23 #include <stdarg.h>
25 #include "windef.h"
26 #include "wingdi.h"
27 #include "winbase.h"
28 #include "winuser.h"
30 #include "objbase.h"
31 #include "ocidl.h"
32 #include "wincodecsdk.h"
33 #include "wine/heap.h"
34 #include "wine/list.h"
36 #include "gdiplus.h"
38 #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
40 #define GP_DEFAULT_PENSTYLE (PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT | PS_JOIN_MITER)
41 #define MAX_ARC_PTS (13)
42 #define MAX_DASHLEN (16) /* this is a limitation of gdi */
43 #define INCH_HIMETRIC (2540)
45 #define VERSION_MAGIC 0xdbc01001
46 #define VERSION_MAGIC2 0xdbc01002
47 #define VALID_MAGIC(x) (((x) & 0xfffff000) == 0xdbc01000)
48 #define TENSION_CONST (0.3)
50 #define GIF_DISPOSE_UNSPECIFIED 0
51 #define GIF_DISPOSE_DO_NOT_DISPOSE 1
52 #define GIF_DISPOSE_RESTORE_TO_BKGND 2
53 #define GIF_DISPOSE_RESTORE_TO_PREV 3
56 COLORREF ARGB2COLORREF(ARGB color) DECLSPEC_HIDDEN;
57 HBITMAP ARGB2BMP(ARGB color) DECLSPEC_HIDDEN;
58 extern INT arc2polybezier(GpPointF * points, REAL x1, REAL y1, REAL x2, REAL y2,
59 REAL startAngle, REAL sweepAngle) DECLSPEC_HIDDEN;
60 extern REAL gdiplus_atan2(REAL dy, REAL dx) DECLSPEC_HIDDEN;
61 extern GpStatus hresult_to_status(HRESULT res) DECLSPEC_HIDDEN;
62 extern REAL units_to_pixels(REAL units, GpUnit unit, REAL dpi) DECLSPEC_HIDDEN;
63 extern REAL pixels_to_units(REAL pixels, GpUnit unit, REAL dpi) DECLSPEC_HIDDEN;
64 extern REAL units_scale(GpUnit from, GpUnit to, REAL dpi) DECLSPEC_HIDDEN;
66 #define WineCoordinateSpaceGdiDevice ((GpCoordinateSpace)4)
68 extern GpStatus gdi_transform_acquire(GpGraphics *graphics);
69 extern GpStatus gdi_transform_release(GpGraphics *graphics);
70 extern GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
71 GpCoordinateSpace src_space, GpMatrix *matrix) DECLSPEC_HIDDEN;
72 extern GpStatus gdip_transform_points(GpGraphics *graphics, GpCoordinateSpace dst_space,
73 GpCoordinateSpace src_space, GpPointF *points, INT count) DECLSPEC_HIDDEN;
75 extern GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics) DECLSPEC_HIDDEN;
76 extern GpStatus encode_image_png(GpImage *image, IStream* stream, GDIPCONST EncoderParameters* params) DECLSPEC_HIDDEN;
78 extern GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result) DECLSPEC_HIDDEN;
79 extern GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc) DECLSPEC_HIDDEN;
80 extern GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc) DECLSPEC_HIDDEN;
81 extern GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color) DECLSPEC_HIDDEN;
82 extern GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
83 GDIPCONST GpRectF* rects, INT count) DECLSPEC_HIDDEN;
84 extern GpStatus METAFILE_SetClipRect(GpMetafile* metafile,
85 REAL x, REAL y, REAL width, REAL height, CombineMode mode) DECLSPEC_HIDDEN;
86 extern GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode) DECLSPEC_HIDDEN;
87 extern GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale) DECLSPEC_HIDDEN;
88 extern GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform) DECLSPEC_HIDDEN;
89 extern GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order) DECLSPEC_HIDDEN;
90 extern GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order) DECLSPEC_HIDDEN;
91 extern GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order) DECLSPEC_HIDDEN;
92 extern GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order) DECLSPEC_HIDDEN;
93 extern GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile) DECLSPEC_HIDDEN;
94 extern GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
95 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex) DECLSPEC_HIDDEN;
96 extern GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex) DECLSPEC_HIDDEN;
97 extern GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex) DECLSPEC_HIDDEN;
98 extern GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex) DECLSPEC_HIDDEN;
99 extern GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex) DECLSPEC_HIDDEN;
100 extern GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile) DECLSPEC_HIDDEN;
101 extern GpStatus METAFILE_DrawImagePointsRect(GpMetafile* metafile, GpImage *image,
102 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
103 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
104 DrawImageAbort callback, VOID *callbackData) DECLSPEC_HIDDEN;
105 extern GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val) DECLSPEC_HIDDEN;
106 extern GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path) DECLSPEC_HIDDEN;
107 extern GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path) DECLSPEC_HIDDEN;
108 extern void METAFILE_Free(GpMetafile *metafile) DECLSPEC_HIDDEN;
110 extern void calc_curve_bezier(const GpPointF *pts, REAL tension, REAL *x1,
111 REAL *y1, REAL *x2, REAL *y2) DECLSPEC_HIDDEN;
112 extern void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj,
113 REAL tension, REAL *x, REAL *y) DECLSPEC_HIDDEN;
115 extern void free_installed_fonts(void) DECLSPEC_HIDDEN;
117 extern BOOL lengthen_path(GpPath *path, INT len) DECLSPEC_HIDDEN;
119 extern DWORD write_region_data(const GpRegion *region, void *data) DECLSPEC_HIDDEN;
120 extern DWORD write_path_data(GpPath *path, void *data) DECLSPEC_HIDDEN;
122 extern GpStatus trace_path(GpGraphics *graphics, GpPath *path) DECLSPEC_HIDDEN;
124 typedef struct region_element region_element;
125 extern void delete_element(region_element *element) DECLSPEC_HIDDEN;
127 extern GpStatus get_hatch_data(GpHatchStyle hatchstyle, const char **result) DECLSPEC_HIDDEN;
129 static inline INT gdip_round(REAL x)
131 return (INT) floorf(x + 0.5);
134 static inline INT ceilr(REAL x)
136 return (INT) ceilf(x);
139 static inline REAL deg2rad(REAL degrees)
141 return M_PI * degrees / 180.0;
144 static inline ARGB color_over(ARGB bg, ARGB fg)
146 BYTE b, g, r, a;
147 BYTE bg_alpha, fg_alpha;
149 fg_alpha = (fg>>24)&0xff;
151 if (fg_alpha == 0xff) return fg;
153 if (fg_alpha == 0) return bg;
155 bg_alpha = (((bg>>24)&0xff) * (0xff-fg_alpha)) / 0xff;
157 if (bg_alpha == 0) return fg;
159 a = bg_alpha + fg_alpha;
160 b = ((bg&0xff)*bg_alpha + (fg&0xff)*fg_alpha)/a;
161 g = (((bg>>8)&0xff)*bg_alpha + ((fg>>8)&0xff)*fg_alpha)/a;
162 r = (((bg>>16)&0xff)*bg_alpha + ((fg>>16)&0xff)*fg_alpha)/a;
164 return (a<<24)|(r<<16)|(g<<8)|b;
167 /* fg is premult, bg and return value are not */
168 static inline ARGB color_over_fgpremult(ARGB bg, ARGB fg)
170 BYTE b, g, r, a;
171 BYTE bg_alpha, fg_alpha;
173 fg_alpha = (fg>>24)&0xff;
175 if (fg_alpha == 0) return bg;
177 bg_alpha = (((bg>>24)&0xff) * (0xff-fg_alpha)) / 0xff;
179 a = bg_alpha + fg_alpha;
180 b = ((bg&0xff)*bg_alpha + (fg&0xff)*0xff)/a;
181 g = (((bg>>8)&0xff)*bg_alpha + ((fg>>8)&0xff)*0xff)/a;
182 r = (((bg>>16)&0xff)*bg_alpha + ((fg>>16)&0xff)*0xff)/a;
184 return (a<<24)|(r<<16)|(g<<8)|b;
187 extern const char *debugstr_rectf(const RectF* rc) DECLSPEC_HIDDEN;
189 extern const char *debugstr_pointf(const PointF* pt) DECLSPEC_HIDDEN;
191 extern void convert_32bppARGB_to_32bppPARGB(UINT width, UINT height,
192 BYTE *dst_bits, INT dst_stride, const BYTE *src_bits, INT src_stride) DECLSPEC_HIDDEN;
194 extern GpStatus convert_pixels(INT width, INT height,
195 INT dst_stride, BYTE *dst_bits, PixelFormat dst_format,
196 INT src_stride, const BYTE *src_bits, PixelFormat src_format, ColorPalette *palette) DECLSPEC_HIDDEN;
198 extern PixelFormat apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data,
199 UINT width, UINT height, INT stride, ColorAdjustType type, PixelFormat fmt) DECLSPEC_HIDDEN;
201 struct GpMatrix{
202 REAL matrix[6];
205 struct GpPen{
206 UINT style;
207 GpUnit unit;
208 REAL width;
209 GpLineCap endcap;
210 GpLineCap startcap;
211 GpDashCap dashcap;
212 GpCustomLineCap *customstart;
213 GpCustomLineCap *customend;
214 GpLineJoin join;
215 REAL miterlimit;
216 GpDashStyle dash;
217 REAL *dashes;
218 INT numdashes;
219 REAL offset; /* dash offset */
220 GpBrush *brush;
221 GpPenAlignment align;
222 GpMatrix transform;
225 struct GpGraphics{
226 HDC hdc;
227 HWND hwnd;
228 BOOL owndc;
229 BOOL alpha_hdc;
230 GpImage *image;
231 ImageType image_type;
232 SmoothingMode smoothing;
233 CompositingQuality compqual;
234 InterpolationMode interpolation;
235 PixelOffsetMode pixeloffset;
236 CompositingMode compmode;
237 TextRenderingHint texthint;
238 GpUnit unit; /* page unit */
239 REAL scale; /* page scale */
240 REAL xres, yres;
241 GpMatrix worldtrans; /* world transform */
242 BOOL busy; /* hdc handle obtained by GdipGetDC */
243 GpRegion *clip; /* in device coords */
244 UINT textcontrast; /* not used yet. get/set only */
245 struct list containers;
246 GraphicsContainer contid; /* last-issued container ID */
247 INT origin_x, origin_y;
248 INT gdi_transform_acquire_count, gdi_transform_save;
249 GpMatrix gdi_transform;
250 HRGN gdi_clip;
251 /* For giving the caller an HDC when we technically can't: */
252 HBITMAP temp_hbitmap;
253 int temp_hbitmap_width;
254 int temp_hbitmap_height;
255 BYTE *temp_bits;
256 HDC temp_hdc;
259 struct GpBrush{
260 GpBrushType bt;
263 struct GpHatch{
264 GpBrush brush;
265 GpHatchStyle hatchstyle;
266 ARGB forecol;
267 ARGB backcol;
270 struct GpSolidFill{
271 GpBrush brush;
272 ARGB color;
275 struct GpPathGradient{
276 GpBrush brush;
277 GpPath* path;
278 ARGB centercolor;
279 GpWrapMode wrap;
280 BOOL gamma;
281 GpPointF center;
282 GpPointF focus;
283 REAL* blendfac; /* blend factors */
284 REAL* blendpos; /* blend positions */
285 INT blendcount;
286 ARGB *surroundcolors;
287 INT surroundcolorcount;
288 ARGB* pblendcolor; /* preset blend colors */
289 REAL* pblendpos; /* preset blend positions */
290 INT pblendcount;
291 GpMatrix transform;
294 struct GpLineGradient{
295 GpBrush brush;
296 GpPointF startpoint;
297 GpPointF endpoint;
298 ARGB startcolor;
299 ARGB endcolor;
300 RectF rect;
301 GpWrapMode wrap;
302 BOOL gamma;
303 REAL* blendfac; /* blend factors */
304 REAL* blendpos; /* blend positions */
305 INT blendcount;
306 ARGB* pblendcolor; /* preset blend colors */
307 REAL* pblendpos; /* preset blend positions */
308 INT pblendcount;
309 GpMatrix transform;
312 struct GpTexture{
313 GpBrush brush;
314 GpMatrix transform;
315 GpImage *image;
316 GpImageAttributes *imageattributes;
317 BYTE *bitmap_bits; /* image bits converted to ARGB and run through imageattributes */
320 struct GpPath{
321 GpFillMode fill;
322 GpPathData pathdata;
323 BOOL newfigure; /* whether the next drawing action starts a new figure */
324 INT datalen; /* size of the arrays in pathdata */
327 struct GpPathIterator{
328 GpPathData pathdata;
329 INT subpath_pos; /* for NextSubpath methods */
330 INT marker_pos; /* for NextMarker methods */
331 INT pathtype_pos; /* for NextPathType methods */
334 struct GpCustomLineCap{
335 CustomLineCapType type;
336 GpPathData pathdata;
337 BOOL fill; /* TRUE for fill, FALSE for stroke */
338 GpLineCap cap; /* as far as I can tell, this value is ignored */
339 REAL inset; /* how much to adjust the end of the line */
340 GpLineJoin join;
341 REAL scale;
344 struct GpAdjustableArrowCap{
345 GpCustomLineCap cap;
348 struct GpImage{
349 IWICBitmapDecoder *decoder;
350 ImageType type;
351 GUID format;
352 UINT flags;
353 UINT frame_count, current_frame;
354 ColorPalette *palette;
355 REAL xres, yres;
356 LONG busy;
359 #define EmfPlusObjectTableSize 64
361 typedef enum EmfPlusObjectType
363 ObjectTypeInvalid,
364 ObjectTypeBrush,
365 ObjectTypePen,
366 ObjectTypePath,
367 ObjectTypeRegion,
368 ObjectTypeImage,
369 ObjectTypeFont,
370 ObjectTypeStringFormat,
371 ObjectTypeImageAttributes,
372 ObjectTypeCustomLineCap,
373 ObjectTypeMax = ObjectTypeCustomLineCap,
374 } EmfPlusObjectType;
376 /* Deserialized EmfPlusObject record. */
377 struct emfplus_object {
378 EmfPlusObjectType type;
379 union {
380 GpBrush *brush;
381 GpPen *pen;
382 GpPath *path;
383 GpRegion *region;
384 GpImage *image;
385 GpFont *font;
386 GpImageAttributes *image_attributes;
387 void *object;
388 } u;
391 struct GpMetafile{
392 GpImage image;
393 GpRectF bounds;
394 GpUnit unit;
395 MetafileType metafile_type;
396 HENHMETAFILE hemf;
397 int preserve_hemf; /* if true, hemf belongs to the app and should not be deleted */
399 /* recording */
400 HDC record_dc;
401 GpGraphics *record_graphics;
402 BYTE *comment_data;
403 DWORD comment_data_size;
404 DWORD comment_data_length;
405 IStream *record_stream;
406 BOOL auto_frame; /* If true, determine the frame automatically */
407 GpPointF auto_frame_min, auto_frame_max;
408 DWORD next_object_id;
410 /* playback */
411 GpGraphics *playback_graphics;
412 HDC playback_dc;
413 GpPointF playback_points[3];
414 GpRectF src_rect;
415 HANDLETABLE *handle_table;
416 int handle_count;
417 XFORM gdiworldtransform;
418 GpMatrix *world_transform;
419 GpUnit page_unit;
420 REAL page_scale;
421 GpRegion *base_clip; /* clip region in device space for all metafile output */
422 GpRegion *clip; /* clip region within the metafile */
423 struct list containers;
424 struct emfplus_object objtable[EmfPlusObjectTableSize];
427 struct GpBitmap{
428 GpImage image;
429 INT width;
430 INT height;
431 PixelFormat format;
432 ImageLockMode lockmode;
433 BYTE *bitmapbits; /* pointer to the buffer we passed in BitmapLockBits */
434 HBITMAP hbitmap;
435 HDC hdc;
436 BYTE *bits; /* actual image bits if this is a DIB */
437 INT stride; /* stride of bits if this is a DIB */
438 BYTE *own_bits; /* image bits that need to be freed with this object */
439 INT lockx, locky; /* X and Y coordinates of the rect when a bitmap is locked for writing. */
440 IWICMetadataReader *metadata_reader; /* NULL if there is no metadata */
441 UINT prop_count;
442 PropertyItem *prop_item; /* cached image properties */
445 struct GpCachedBitmap{
446 GpImage *image;
449 struct color_key{
450 BOOL enabled;
451 ARGB low;
452 ARGB high;
455 struct color_matrix{
456 BOOL enabled;
457 ColorMatrixFlags flags;
458 ColorMatrix colormatrix;
459 ColorMatrix graymatrix;
462 struct color_remap_table{
463 BOOL enabled;
464 INT mapsize;
465 ColorMap *colormap;
468 enum imageattr_noop{
469 IMAGEATTR_NOOP_UNDEFINED,
470 IMAGEATTR_NOOP_SET,
471 IMAGEATTR_NOOP_CLEAR,
474 struct GpImageAttributes{
475 WrapMode wrap;
476 ARGB outside_color;
477 BOOL clamp;
478 struct color_key colorkeys[ColorAdjustTypeCount];
479 struct color_matrix colormatrices[ColorAdjustTypeCount];
480 struct color_remap_table colorremaptables[ColorAdjustTypeCount];
481 BOOL gamma_enabled[ColorAdjustTypeCount];
482 REAL gamma[ColorAdjustTypeCount];
483 enum imageattr_noop noop[ColorAdjustTypeCount];
486 struct GpFont{
487 GpFontFamily *family;
488 OUTLINETEXTMETRICW otm;
489 REAL emSize; /* in font units */
490 Unit unit;
493 extern const struct GpStringFormat default_drawstring_format DECLSPEC_HIDDEN;
495 struct GpStringFormat{
496 INT attr;
497 LANGID lang;
498 LANGID digitlang;
499 StringAlignment align;
500 StringTrimming trimming;
501 HotkeyPrefix hkprefix;
502 StringAlignment line_align;
503 StringDigitSubstitute digitsub;
504 INT tabcount;
505 REAL firsttab;
506 REAL *tabs;
507 CharacterRange *character_ranges;
508 INT range_count;
509 BOOL generic_typographic;
512 extern void init_generic_string_formats(void) DECLSPEC_HIDDEN;
513 extern void free_generic_string_formats(void) DECLSPEC_HIDDEN;
515 struct GpFontCollection{
516 GpFontFamily **FontFamilies;
517 INT count;
518 INT allocated;
521 struct GpFontFamily{
522 WCHAR FamilyName[LF_FACESIZE];
523 UINT16 em_height, ascent, descent, line_spacing; /* in font units */
524 int dpi;
527 /* internal use */
528 typedef enum RegionType
530 RegionDataRect = 0x10000000,
531 RegionDataPath = 0x10000001,
532 RegionDataEmptyRect = 0x10000002,
533 RegionDataInfiniteRect = 0x10000003,
534 } RegionType;
536 struct region_element
538 DWORD type; /* Rectangle, Path, SpecialRectangle, or CombineMode */
539 union
541 GpRectF rect;
542 GpPath *path;
543 struct
545 struct region_element *left; /* the original region */
546 struct region_element *right; /* what *left was combined with */
547 } combine;
548 } elementdata;
551 struct GpRegion{
552 DWORD num_children;
553 region_element node;
556 struct memory_buffer
558 const BYTE *buffer;
559 INT size, pos;
562 static inline void init_memory_buffer(struct memory_buffer *mbuf, const BYTE *buffer, INT size)
564 mbuf->buffer = buffer;
565 mbuf->size = size;
566 mbuf->pos = 0;
569 static inline const void *buffer_read(struct memory_buffer *mbuf, INT size)
571 if (mbuf->size - mbuf->pos >= size)
573 const void *data = mbuf->buffer + mbuf->pos;
574 mbuf->pos += size;
575 return data;
577 return NULL;
580 typedef GpStatus (*gdip_format_string_callback)(HDC hdc,
581 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
582 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
583 INT lineno, const RectF *bounds, INT *underlined_indexes,
584 INT underlined_index_count, void *user_data);
586 GpStatus gdip_format_string(HDC hdc,
587 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
588 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip,
589 gdip_format_string_callback callback, void *user_data) DECLSPEC_HIDDEN;
591 void get_log_fontW(const GpFont *, GpGraphics *, LOGFONTW *) DECLSPEC_HIDDEN;
593 static inline BOOL image_lock(GpImage *image, BOOL *unlock)
595 LONG tid = GetCurrentThreadId(), owner_tid;
596 owner_tid = InterlockedCompareExchange(&image->busy, tid, 0);
597 *unlock = !owner_tid;
598 return !owner_tid || owner_tid==tid;
601 static inline void image_unlock(GpImage *image, BOOL unlock)
603 if (unlock) image->busy = 0;
606 #endif