fixes to gdip, see ChangeLog for details
[mono.git] / mcs / class / System.Drawing / gdiplus / bitmap.c
1 /* 
2  * bitmap.c
3  * 
4  * Copyright (c) 2003 Alexandre Pigolkine
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
7  * and associated documentation files (the "Software"), to deal in the Software without restriction, 
8  * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
9  * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
10  * subject to the following conditions:
11  * 
12  * The above copyright notice and this permission notice shall be included in all copies or substantial 
13  * portions of the Software.
14  * 
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
16  * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
18  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
19  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20  * 
21  * Authors:
22  *   Alexandre Pigolkine(pigolkine@gmx.de)
23  */
24
25 #include <glib.h>
26 #include "gdip.h"
27 #include "gdip_win32.h"
28 #include <string.h>
29 #include <unistd.h>
30
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34
35 void 
36 gdip_bitmap_init (GpBitmap *bitmap)
37 {
38         gdip_image_init (&bitmap->image);
39         bitmap->image.type = imageBitmap;
40         bitmap->cairo_format = 0;
41         bitmap->data.Width = 0;
42         bitmap->data.Height = 0;
43         bitmap->data.Stride = 0;
44         bitmap->data.PixelFormat = 0;
45         bitmap->data.Scan0 = 0;
46         bitmap->data.Reserved = 0;
47         
48         bitmap->hBitmapDC = 0;
49         bitmap->hInitialBitmap = 0;
50         bitmap->hBitmap = 0;
51 }
52
53 GpBitmap *
54 gdip_bitmap_new ()
55 {
56         GpBitmap *result = (GpBitmap *) GdipAlloc (sizeof(GpBitmap));
57         gdip_bitmap_init (result);
58         return result;
59 }
60
61 /*
62  * This should only be called from GdipDisposeImage, and it should *not* free
63  * the structure, that one is freed by GdipDisposeImage
64  */
65 void 
66 gdip_bitmap_dispose (GpBitmap *bitmap)
67 {
68         
69 }
70
71 void 
72 gdip_bitmap_fill_info_header (GpBitmap *bitmap, PBITMAPINFOHEADER bmi)
73 {
74         int  bitmapLen = bitmap->data.Stride * bitmap->data.Height;
75         memset (bmi, 0, 40);
76         bmi->biSize = sizeof (BITMAPINFOHEADER);
77         bmi->biWidth = bitmap->data.Width;
78         bmi->biHeight = -bitmap->data.Height;
79         bmi->biPlanes = 1;
80         bmi->biBitCount = 32;
81         bmi->biCompression = BI_RGB;
82         bmi->biSizeImage = bitmapLen; 
83 }
84
85 void 
86 gdip_bitmap_save_bmp (const char *name, GpBitmap *bitmap)
87 {
88         BITMAPFILEHEADER bmfh;
89         BITMAPINFOHEADER bmi;
90         int  bitmapLen = bitmap->data.Stride * bitmap->data.Height;
91         FILE *fp;
92         
93         bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
94         bmfh.bfType = BFT_BITMAP;
95         bmfh.bfOffBits = (14 + 40 + 0 * 4);
96         bmfh.bfSize = (bmfh.bfOffBits + bitmapLen);
97         fp = fopen (name, "w+b");
98         fwrite (&bmfh, 1, sizeof (bmfh), fp);
99         gdip_bitmap_fill_info_header (bitmap, &bmi);
100         bmi.biHeight = -bmi.biHeight;
101         fwrite (&bmi, 1, sizeof (bmi), fp);
102         fwrite (bitmap->data.Scan0, 1, bitmapLen, fp);
103         fclose (fp);
104 }
105
106 void *
107 gdip_bitmap_create_Win32_HDC (GpBitmap *bitmap)
108 {
109         void * result = 0;
110         void * hdc = CreateCompatibleDC_pfn (0);
111         void * hbitmap = 0, * holdbitmap = 0;
112         void * hdcDesc = GetDC_pfn (0);
113                         
114         hbitmap = CreateCompatibleBitmap_pfn (hdcDesc, bitmap->data.Width, bitmap->data.Height);
115         if (hbitmap != 0) {
116                 BITMAPINFO      bmi;
117                 gdip_bitmap_fill_info_header (bitmap, &bmi.bmiHeader);
118                 //_saveBmp ("file1.bmp", bitmap);
119                 SetDIBits_pfn (hdc, hbitmap, 0, bitmap->data.Height, bitmap->data.Scan0, &bmi, 0);
120                 holdbitmap = SelectObject_pfn (hdc, hbitmap);
121                 bitmap->hBitmapDC = hdc;
122                 bitmap->hInitialBitmap = holdbitmap;
123                 bitmap->hBitmap = hbitmap;
124                 result = hdc;
125         }
126         else {
127                 DeleteDC_pfn (hdc);
128         }
129         ReleaseDC_pfn (0, hdcDesc);
130         return result;
131 }
132
133 void 
134 gdip_bitmap_destroy_Win32_HDC (GpBitmap *bitmap, void *hdc)
135 {
136         if (bitmap->hBitmapDC == hdc) {
137                 
138                 BITMAPINFO      bmi;
139                 int res = 0;
140                 unsigned long *array, *end;
141                         
142                 SelectObject_pfn (bitmap->hBitmapDC, bitmap->hInitialBitmap);
143                         
144                 gdip_bitmap_fill_info_header (bitmap, &bmi.bmiHeader);
145                 res = GetDIBits_pfn (bitmap->hBitmapDC, bitmap->hBitmap, 0, bitmap->data.Height, bitmap->data.Scan0, &bmi, 0);
146                 if (bitmap->cairo_format == CAIRO_FORMAT_ARGB32) {
147                         array = bitmap->data.Scan0;
148                         end = array + (bmi.bmiHeader.biSizeImage >> 2);
149                         while (array < end) {
150                                 *array |= 0xff000000;
151                                 ++array;
152                         }
153                 }
154                 //_saveBmp ("file2.bmp", bitmap);
155
156                 DeleteObject_pfn (bitmap->hBitmap);
157                 DeleteDC_pfn (bitmap->hBitmapDC);
158                 bitmap->hBitmapDC = 0;
159                 bitmap->hInitialBitmap = 0;
160                 bitmap->hBitmap = 0;
161         }
162 }
163
164 GpStatus 
165 GdipCreateBitmapFromScan0 (int width, int height, int stride, int format, void *scan0, GpBitmap **bitmap)
166 {
167         GpBitmap *result = 0;
168         int cairo_format = 0;
169
170         if (stride == 0)
171                 return InvalidParameter;
172         if (scan0 == NULL)
173                 return InvalidParameter;
174                         
175         switch (format) {
176         case Format24bppRgb:
177                 cairo_format = CAIRO_FORMAT_RGB24;      
178         break;
179         case Format32bppArgb:
180                 cairo_format = CAIRO_FORMAT_ARGB32;     
181         break;
182         default:
183                 *bitmap = 0;
184                 return NotImplemented;
185         }
186         result = gdip_bitmap_new ();
187         result->cairo_format = cairo_format;
188         result->data.Width = width;
189         result->data.Height = height;
190         result->data.Stride = stride;
191         result->data.PixelFormat = format;
192         result->data.Scan0 = scan0;
193         
194         *bitmap = result;
195         return Ok;
196 }
197
198 GpStatus
199 GdipCreateBitmapFromGraphics (int width, int height, GpGraphics *graphics, GpBitmap **bitmap)
200 {
201         GpBitmap *result = 0;
202         int bmpSize = 0;
203         int cairo_format = 0;
204         int stride = width;
205
206         /*
207          * FIXME: should get the stride based on the format of the graphics object.
208          */
209         fprintf (stderr, "GdipCreateBitmapFromGraphics: This routine has not been checked for stride size\n");
210         while (stride % 4)
211                 stride++;
212         
213         stride *= 4;
214         cairo_format = CAIRO_FORMAT_ARGB32;     
215         bmpSize = stride * height;
216         result = gdip_bitmap_new ();
217         result->cairo_format = cairo_format;
218         result->data.Width = width;
219         result->data.Height = height;
220         result->data.Stride = stride;
221         result->data.PixelFormat = Format32bppArgb;
222         result->data.Scan0 = GdipAlloc (bmpSize);
223         result->data.Reserved = 1;
224         *bitmap = result;
225         return Ok;
226 }
227
228 GpStatus
229 GdipCloneBitmapAreaI (int x, int y, int width, int height, int format, GpBitmap *original, GpBitmap **bitmap)
230 {
231         GpBitmap *result = 0;
232         int bmpSize = original->data.Height * original->data.Stride;
233
234         /*
235          * FIXME: Convert format if needed and copy only specified rectangle
236          */ 
237         result = gdip_bitmap_new ();
238         result->cairo_format = original->cairo_format;
239         result->data.Width = original->data.Width;
240         result->data.Height = original->data.Height;
241         result->data.Stride = original->data.Stride;
242         result->data.PixelFormat = original->data.PixelFormat;
243         result->data.Scan0 = GdipAlloc (bmpSize);
244         memmove (result->data.Scan0, original->data.Scan0, bmpSize);
245         result->data.Reserved = 1;
246         
247         *bitmap = result;
248         return Ok;
249 }
250
251 static int 
252 ChangePixelFormat (GpBitmap *bitmap, GdipBitmapData *destData) 
253 {
254         int     sourcePixelIncrement = (bitmap->data.PixelFormat == Format32bppArgb) ? 1 : 0;
255         int     destinationPixelIncrement = (destData->PixelFormat == Format32bppArgb) ? 1 : 0;
256         char *  curSrc = bitmap->data.Scan0;
257         char *  curDst = 0;
258         int     i, j;
259         
260         /*
261         printf("ChangePixelFormat to %d. Src inc %d, Dest Inc %d\n", 
262                 destData->PixelFormat, sourcePixelIncrement, destinationPixelIncrement);
263         */
264         if (bitmap->data.PixelFormat == destData->PixelFormat) return 0;
265         
266         if (destData->PixelFormat != Format32bppArgb && destData->PixelFormat != Format24bppRgb) {
267                 fprintf (stderr, "This pixel format is not supported %d\n", destData->PixelFormat);
268                 return 0;
269         }
270         destData->Width = bitmap->data.Width;
271         destData->Height = bitmap->data.Height;
272         destData->Stride = ((destData->PixelFormat == Format32bppArgb ) ? 4 : 3 ) * bitmap->data.Width;
273         destData->Scan0 = GdipAlloc (destData->Stride * bitmap->data.Height);
274         destData->Reserved = 1;
275         curSrc = bitmap->data.Scan0;
276         curDst = (char *)destData->Scan0;
277         for ( i = 0; i < bitmap->data.Height; i++) {
278                 for( j = 0; j < bitmap->data.Width; j++) {
279                         *curDst++ = *curSrc++;
280                         *curDst++ = *curSrc++;
281                         *curDst++ = *curSrc++;
282                         curSrc += sourcePixelIncrement;
283                         curDst += destinationPixelIncrement;
284                 }
285         }
286         return 1;
287 }
288
289 GpStatus 
290 GdipBitmapLockBits (GpBitmap *bitmap, Rect *rc, int flags, int format, GdipBitmapData *result)
291 {
292         if (bitmap == 0){
293                 printf ("Bitmap is null\n");
294                 return InvalidParameter;
295         }
296
297         /* Special case: the entire image is requested */
298         if (rc->left == 0 && rc->right == bitmap->data.Width &&
299             rc->top == 0 && rc->bottom == bitmap->data.Height &&
300             format == bitmap->data.PixelFormat){
301                 *result = bitmap->data;
302                 result->Reserved = result->Reserved & ~1;
303                 return Ok;
304         }
305         
306         if (bitmap->data.PixelFormat != format) {
307                 GdipBitmapData          convert;
308                 convert.PixelFormat = format;
309                 if (!ChangePixelFormat (bitmap, &convert)) {
310                         printf ("Requesting format change, not supported yet %d %d\n", bitmap->data.PixelFormat, format);
311                         return InvalidParameter;
312                 }
313                 result->Width = convert.Width; 
314                 result->Height = convert.Height; 
315                 result->Stride = convert.Stride; 
316                 result->PixelFormat = convert.PixelFormat; 
317                 result->Reserved = convert.Reserved; 
318                 result->Scan0 = convert.Scan0;
319         }
320         else {
321                 result->Width = bitmap->data.Width; 
322                 result->Height = bitmap->data.Height; 
323                 result->Stride = bitmap->data.Stride; 
324                 result->PixelFormat = bitmap->data.PixelFormat; 
325                 result->Reserved = bitmap->data.Reserved; 
326                 result->Scan0 = bitmap->data.Scan0;
327         }
328
329         return Ok;
330 }
331
332 GpStatus 
333 ____BitmapLockBits (GpBitmap *bitmap, Rect *rc, int flags, int format, int *width, int *height, int *stride, int *fptr, int *res, int *scan0)
334 {
335         GdipBitmapData d;
336         int s;
337         
338         s = GdipBitmapLockBits (bitmap, rc, flags, format, &d);
339         *width = d.Width;
340         *height = d.Height;
341         *stride = d.Stride;
342         *fptr = d.PixelFormat;
343         *res = d.Reserved;
344         *scan0 = (int)d.Scan0;
345
346         return s;
347 }
348
349 GpStatus 
350 GdipBitmapUnlockBits (GpBitmap *bitmap, GdipBitmapData *bitmap_data)
351 {
352         if (bitmap_data->Reserved & 1)
353                 GdipFree (bitmap_data->Scan0);
354         return Ok;
355 }
356