e66132457ee3560c9979ca822d11604410191378
[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         GdipFree (bitmap->data.Scan0);
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
173         if (scan0 == NULL)
174                 scan0 = GdipAlloc (stride*height);
175                         
176         switch (format) {
177         case Format24bppRgb:
178                 cairo_format = CAIRO_FORMAT_RGB24;      
179         break;
180         case Format32bppArgb:
181                 cairo_format = CAIRO_FORMAT_ARGB32;     
182         break;
183         default:
184                 *bitmap = 0;
185                 return NotImplemented;
186         }
187         result = gdip_bitmap_new ();
188         result->cairo_format = cairo_format;
189         result->data.Width = width;
190         result->data.Height = height;
191         result->data.Stride = stride;
192         result->data.PixelFormat = format;
193         result->data.Scan0 = scan0;
194         
195         *bitmap = result;
196         return Ok;
197 }
198
199 GpStatus
200 GdipCreateBitmapFromGraphics (int width, int height, GpGraphics *graphics, GpBitmap **bitmap)
201 {
202         GpBitmap *result = 0;
203         int bmpSize = 0;
204         int cairo_format = 0;
205         int stride = width;
206
207         /*
208          * FIXME: should get the stride based on the format of the graphics object.
209          */
210         fprintf (stderr, "GdipCreateBitmapFromGraphics: This routine has not been checked for stride size\n");
211         while (stride % 4)
212                 stride++;
213         
214         stride *= 4;
215         cairo_format = CAIRO_FORMAT_ARGB32;     
216         bmpSize = stride * height;
217         result = gdip_bitmap_new ();
218         result->cairo_format = cairo_format;
219         result->data.Width = width;
220         result->data.Height = height;
221         result->data.Stride = stride;
222         result->data.PixelFormat = Format32bppArgb;
223         result->data.Scan0 = GdipAlloc (bmpSize);
224         result->data.Reserved = 1;
225         *bitmap = result;
226         return Ok;
227 }
228
229 GpStatus
230 GdipCloneBitmapAreaI (int x, int y, int width, int height, int format, GpBitmap *original, GpBitmap **bitmap)
231 {
232         GpBitmap *result = 0;
233         int bmpSize = original->data.Height * original->data.Stride;
234
235         /*
236          * FIXME: Convert format if needed and copy only specified rectangle
237          */ 
238         result = gdip_bitmap_new ();
239         result->cairo_format = original->cairo_format;
240         result->data.Width = original->data.Width;
241         result->data.Height = original->data.Height;
242         result->data.Stride = original->data.Stride;
243         result->data.PixelFormat = original->data.PixelFormat;
244         result->data.Scan0 = GdipAlloc (bmpSize);
245         memmove (result->data.Scan0, original->data.Scan0, bmpSize);
246         result->data.Reserved = 1;
247         
248         *bitmap = result;
249         return Ok;
250 }
251
252 static int 
253 ChangePixelFormat (GpBitmap *bitmap, GdipBitmapData *destData) 
254 {
255         int     sourcePixelIncrement = (bitmap->data.PixelFormat == Format32bppArgb) ? 1 : 0;
256         int     destinationPixelIncrement = (destData->PixelFormat == Format32bppArgb) ? 1 : 0;
257         char *  curSrc = bitmap->data.Scan0;
258         char *  curDst = 0;
259         int     i, j;
260         
261         /*
262         printf("ChangePixelFormat to %d. Src inc %d, Dest Inc %d\n", 
263                 destData->PixelFormat, sourcePixelIncrement, destinationPixelIncrement);
264         */
265         if (bitmap->data.PixelFormat == destData->PixelFormat) return 0;
266         
267         if (destData->PixelFormat != Format32bppArgb && destData->PixelFormat != Format24bppRgb) {
268                 fprintf (stderr, "This pixel format is not supported %d\n", destData->PixelFormat);
269                 return 0;
270         }
271         destData->Width = bitmap->data.Width;
272         destData->Height = bitmap->data.Height;
273         destData->Stride = ((destData->PixelFormat == Format32bppArgb ) ? 4 : 3 ) * bitmap->data.Width;
274         destData->Scan0 = GdipAlloc (destData->Stride * bitmap->data.Height);
275         destData->Reserved = 1;
276         curSrc = bitmap->data.Scan0;
277         curDst = (char *)destData->Scan0;
278         for ( i = 0; i < bitmap->data.Height; i++) {
279                 for( j = 0; j < bitmap->data.Width; j++) {
280                         *curDst++ = *curSrc++;
281                         *curDst++ = *curSrc++;
282                         *curDst++ = *curSrc++;
283                         curSrc += sourcePixelIncrement;
284                         curDst += destinationPixelIncrement;
285                 }
286         }
287         return 1;
288 }
289
290 GpStatus 
291 GdipBitmapLockBits (GpBitmap *bitmap, Rect *rc, int flags, int format, GdipBitmapData *result)
292 {
293         if (bitmap == 0){
294                 printf ("Bitmap is null\n");
295                 return InvalidParameter;
296         }
297
298         /* Special case: the entire image is requested */
299         if (rc->left == 0 && rc->right == bitmap->data.Width &&
300             rc->top == 0 && rc->bottom == bitmap->data.Height &&
301             format == bitmap->data.PixelFormat){
302                 *result = bitmap->data;
303                 result->Reserved = result->Reserved & ~1;
304                 return Ok;
305         }
306         
307         if (bitmap->data.PixelFormat != format) {
308                 GdipBitmapData          convert;
309                 convert.PixelFormat = format;
310                 if (!ChangePixelFormat (bitmap, &convert)) {
311                         printf ("Requesting format change, not supported yet %d %d\n", bitmap->data.PixelFormat, format);
312                         return InvalidParameter;
313                 }
314                 result->Width = convert.Width; 
315                 result->Height = convert.Height; 
316                 result->Stride = convert.Stride; 
317                 result->PixelFormat = convert.PixelFormat; 
318                 result->Reserved = convert.Reserved; 
319                 result->Scan0 = convert.Scan0;
320         }
321         else {
322                 result->Width = bitmap->data.Width; 
323                 result->Height = bitmap->data.Height; 
324                 result->Stride = bitmap->data.Stride; 
325                 result->PixelFormat = bitmap->data.PixelFormat; 
326                 result->Reserved = bitmap->data.Reserved; 
327                 result->Scan0 = bitmap->data.Scan0;
328         }
329
330         return Ok;
331 }
332
333 GpStatus 
334 ____BitmapLockBits (GpBitmap *bitmap, Rect *rc, int flags, int format, int *width, int *height, int *stride, int *fptr, int *res, int *scan0)
335 {
336         GdipBitmapData d;
337         int s;
338         
339         s = GdipBitmapLockBits (bitmap, rc, flags, format, &d);
340         *width = d.Width;
341         *height = d.Height;
342         *stride = d.Stride;
343         *fptr = d.PixelFormat;
344         *res = d.Reserved;
345         *scan0 = (int)d.Scan0;
346
347         return s;
348 }
349
350 GpStatus 
351 GdipBitmapUnlockBits (GpBitmap *bitmap, GdipBitmapData *bitmap_data)
352 {
353         if (bitmap_data->Reserved & 1)
354                 GdipFree (bitmap_data->Scan0);
355         return Ok;
356 }
357