Coin Logo Coin3D is Free Software,
published under the BSD 3-clause license.
https://coin3d.github.io
https://www.kongsberg.com/en/kogt/
simage_gdiplus.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) Kongsberg Oil & Gas Technologies
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /*
18  * GDI+ provides support for the following image formats:
19  * bmp, gif, jpeg, exif, png, tiff, icon, wmf, emf
20  * Only the raster images are used through usage of the Bitmap class.
21  *
22  * It amazes me everytime anew, whenever I get exposed to them, how
23  * much a Microsoft API manages to suck stronger than the biggest
24  * black hole known to mankind... 20060415 tamer.
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif /* HAVE_CONFIG_H */
30 
31 #include <simage_gdiplus.h>
32 
33 #include <windows.h>
34 
35 /* MSVC6 fix for gdiplus.h */
36 #if defined(_MSC_VER) && (_MSC_VER == 1200) && !defined(ULONG_PTR)
37 #define ULONG_PTR ULONG /* (32bit build) */
38 #endif /* MSVC6 */
39 #include <gdiplus.h>
40 
41 #include <stdio.h>
42 #include <string.h>
43 #include <ctype.h>
44 #include <stdlib.h>
45 #include <assert.h>
46 
47 #ifdef HAVE_GDIPLUS_LOCKBITS_RECTARG_POINTER
48 #define LOCKBITS_RECT_CAST(arg) &arg
49 #else // !GDIPVER
50 /* Old VC6 legacy download from codeproject.com with slightly
51  different LockBits() signature (Rect by reference). */
52 #define LOCKBITS_RECT_CAST(arg) arg
53 #endif // !GDIPVER
54 
55 enum {
64 };
65 
67 
68 /*
69  * Get the pixel format that should be used for reading the image.
70  *
71  * The component depth and component composition of simage images differ
72  * from the pixel formats used by GDI+. This function maps a Bitmap to
73  * an appropriate PixelFormat that makes it easy for us to copy the
74  * bitmap data returned by Bitmap::LockBits(...) to the simage image.
75  *
76  * The 'grayscale' flag is an output parameter that is used to indicate
77  * that even though the returned pixel format is PixelFormat32bppARGB,
78  * the image is really a gray scale image with alpha channel that can be
79  * converted to a two-component simage image.
80  */
81 static Gdiplus::PixelFormat
82 getReadFormat(Gdiplus::Bitmap & bitmap, bool & grayscale)
83 {
84  grayscale = false;
85  Gdiplus::PixelFormat format = bitmap.GetPixelFormat();
86 
87  if (Gdiplus::IsIndexedPixelFormat(format)) {
88  INT palettesize = bitmap.GetPaletteSize();
89  Gdiplus::ColorPalette * palette =
90  reinterpret_cast<Gdiplus::ColorPalette *>(new char[palettesize]);
91  bitmap.GetPalette(palette, palettesize);
92  bool alphaflag = (palette->Flags & Gdiplus::PaletteFlagsHasAlpha) != 0;
93  bool grayflag = (palette->Flags & Gdiplus::PaletteFlagsGrayScale) != 0;
94  delete[] reinterpret_cast<char *>(palette);
95 
96  if (!grayflag && !alphaflag)
97  return PixelFormat24bppRGB;
98  if (!grayflag && alphaflag)
99  return PixelFormat32bppARGB;
100  if (grayflag && !alphaflag)
101  return PixelFormat16bppGrayScale;
102 
103  // since GDI+ doesn't define a non-indexed color format for
104  // grayscale images with alpha channel, we must extract the
105  // image as ARGB, and convert it to simage's 0xGGAA - format
106  // afterwards.
107  grayscale = true;
108  return PixelFormat32bppARGB;
109  }
110 
111  if (format == PixelFormat16bppGrayScale) {
112  return PixelFormat16bppGrayScale;
113  }
114 
115  if ((format == PixelFormat16bppRGB555) ||
116  (format == PixelFormat16bppRGB565) ||
117  (format == PixelFormat24bppRGB) ||
118  (format == PixelFormat32bppRGB) ||
119  (format == PixelFormat48bppRGB)) {
120  return PixelFormat24bppRGB;
121  }
122 
123  return PixelFormat32bppARGB;
124 }
125 
126 /*
127  * Crate a buffer and copy image data into the buffer.
128  *
129  * Only use this function for RGB or RGBA images, that is
130  * (numcomponents == 3) || (numcomponents == 4).
131  *
132  * The 'stride' parameter is the number of bytes between each
133  * consecutive line in the input buffer.
134  *
135  * The image data is stored in the output buffer in simage
136  * representation: 0xRRGGBBAA and 0xRRGGBB.
137  *
138  * The function will return NULL if malloc fails to allocate
139  * memory for the output buffer.
140  */
141 static unsigned char *
142 copyImageBuffer(unsigned char * src, unsigned int width,
143  unsigned int height,
144  unsigned int numcomponents, unsigned int stride)
145 {
146  assert(src);
147  assert(stride >= (width * numcomponents));
148  assert((numcomponents == 3) || (numcomponents == 4));
149  if ((numcomponents != 3) && (numcomponents != 4)) { return NULL; }
150 
151  unsigned char * dst =
152  (unsigned char *)malloc(width * height * numcomponents);
153  if (!dst) { return NULL; }
154 
155  /* The image must be flipped horizontally so we start writing from the
156  end of the the destination buffer */
157  dst += width * height * numcomponents;
158 
159  if (numcomponents == 3) {
160  for (unsigned int y = 0; y < height; y++) {
161  dst -= width * numcomponents;
162  for (unsigned int x = 0; x < width; ++x) {
163  /* GDI+ stores the image internally as BGR,
164  we store it as RGB */
165  unsigned int offset = numcomponents * x;
166  dst[offset] = src[offset + 2];
167  dst[offset + 1] = src[offset + 1];
168  dst[offset + 2] = src[offset];
169  }
170  src += stride;
171  }
172  } else {
173  for (unsigned int y = 0; y < height; y++) {
174  dst -= width * numcomponents;
175  for (unsigned int x = 0; x < width; ++x) {
176  /* GDI+ stores the image internally as BGRA,
177  we store it as RGBA */
178  unsigned int offset = numcomponents * x;
179  dst[offset] = src[offset + 2];
180  dst[offset + 1] = src[offset + 1];
181  dst[offset + 2] = src[offset];
182  dst[offset + 3] = src[offset + 3];
183  }
184  src += stride;
185  }
186  }
187 
188  return dst;
189 }
190 
191 
192 /*
193  * Crate a buffer and copy image data into the buffer.
194  *
195  * Only use this function for images that GDI+ represent as ARGB,
196  * but simage can store as 0xGGAA.
197  *
198  * The 'stride' parameter is the number of bytes between each
199  * consecutive line in the input buffer.
200  *
201  * The function will return NULL if malloc fails to allocate
202  * memory for the output buffer.
203  */
204 static unsigned char *
205 copy32bppGrayScaleBuffer(unsigned char * src, unsigned int width,
206  unsigned int height, unsigned int stride)
207 {
208  assert(src);
209  assert(stride >= (width * 4));
210 
211  unsigned char * dst = (unsigned char *)malloc(width * height * 2);
212  if (!dst) { return NULL; }
213 
214  /* The image must be flipped horizontally so we start writing from the
215  end of the the destination buffer */
216  dst += width * height * 2;
217 
218  for (unsigned int y = 0; y < height; y++) {
219  dst -= width * 2;
220  for (unsigned int x = 0; x < width * 2;
221  x += 2) {
222  /* a GDI+ buffer stores the components internally in reverse order,
223  eg. ARGB is stored as BGRA. Extract the blue component as the
224  gray value and the alpha value.*/
225  dst[x] = src[x];
226  dst[x + 1] = src[x + 3];
227  }
228  src += stride;
229  }
230 
231  return dst;
232 }
233 
234 /*
235  * Crate a buffer and copy image data into the buffer.
236  *
237  * Only use this function for images that GDI+ represent as 16-bits
238  * gray scale. Simage stores these as 8-bits grayscale.
239  *
240  * The 'stride' parameter is the number of bytes between each
241  * consecutive line in the input buffer.
242  *
243  * The function will return NULL if malloc fails to allocate
244  * memory for the output buffer.
245  */
246 static unsigned char *
247 copy16bppGrayScaleBuffer(unsigned char * src, unsigned int width,
248  unsigned int height, unsigned int stride)
249 {
250  assert(stride >= (2 * width));
251 
252  unsigned char * dst = (unsigned char *)malloc(width * height);
253  if (!dst) { return NULL; }
254 
255  /* The image must be flipped horizontally so we start writing from the
256  end of the the destination buffer */
257  dst += width * height;
258 
259  unsigned short * shortsrc = (unsigned short *)src;
260 
261  for (unsigned int y = 0; y < height; ++y) {
262  dst -= width;
263  for (unsigned int x = 0; x < width; ++x) {
264  /* Compress the 2-byte value to a 1-byte value */
265  dst[x] = (unsigned char)((shortsrc[x] / 65535.0) * 255.0);
266  }
267  shortsrc += stride;
268  }
269 
270  return dst;
271 }
272 
273 static int
275 {
276  static int did_init = 0;
277 
278  if (!did_init) {
279  /* initialize GDI+ */
280 
281  /* Windows 64-bit uses the LLP64 type model, where int and long
282  * are 32-bit and a pointer is 64-bit. Most Unices use the LP64
283  * where int is 32-bit and long and pointer are 64-bit. */
284  /* FIXME: the following solution is a kludge. 20081001 tamer. */
285 #if defined(_WIN64)
286  unsigned long long gdiplusToken = 0;
287 #else
288  unsigned long gdiplusToken = 0;
289 #endif
290  Gdiplus::GdiplusStartupInput gdiplusStartupInput;
291  if (Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) == Gdiplus::Ok) {
292  did_init = 1;
293  }
294  }
295 
296  return did_init;
297 }
298 
299 int
300 simage_gdiplus_error(char * buffer, int buflen)
301 {
302  switch (gdipluserror) {
303  case ERR_INIT:
304  strncpy(buffer, "GDI+ loader: Error initializing GDI+", buflen);
305  break;
306  case ERR_OPEN:
307  strncpy(buffer, "GDI+ loader: Error opening file", buflen);
308  break;
309  case ERR_READ:
310  strncpy(buffer, "GDI+ loader: Error reading file", buflen);
311  break;
312  case ERR_MEM:
313  strncpy(buffer, "GDI+ loader: Out of memory error", buflen);
314  break;
315  case ERR_OPEN_WRITE:
316  strncpy(buffer, "GDI+ saver: Error opening file", buflen);
317  break;
318  case ERR_WRITE:
319  strncpy(buffer, "GDI+ loader: Error writing file", buflen);
320  break;
321  case ERR_NOT_IMPLEMENTED:
322  strncpy(buffer, "GDI+ loader: Feature not implemented", buflen);
323  break;
324  default:
325  strncpy(buffer, "GDI+ loader: Interesting unknown error you got", buflen);
326  }
327 
328  return gdipluserror;
329 }
330 
331 int
332 simage_gdiplus_identify(const char * ptr,
333  const unsigned char * header,
334  int headerlen)
335 {
337  if (!gdiplus_init()) { gdipluserror = ERR_INIT; return 0; }
338 
339  /* convert C string to wide char */
340  wchar_t * filename = new wchar_t[strlen(ptr)+1];
341 
342  if (!filename) { return 0; }
343  mbstowcs(filename, ptr, strlen(ptr)+1);
344 
345  Gdiplus::Bitmap bitmap(filename);
346 
347  if (bitmap.GetFlags() == Gdiplus::ImageFlagsNone) {
348  delete filename; return 0;
349  }
350 
351  delete filename;
352 
353  return 1;
354 }
355 
356 unsigned char *
358  int * width,
359  int * height,
360  int * numcomponents)
361 {
362  Gdiplus::Bitmap * bitmap =
363  (Gdiplus::Bitmap*)simage_gdiplus_open(filename, width, height,
364  numcomponents);
365  if (!bitmap) { return NULL; }
366 
367  bool grayscale = false;
368  Gdiplus::PixelFormat format = getReadFormat(*bitmap, grayscale);
369 
370  Gdiplus::BitmapData bitmapdata;
371  Gdiplus::Rect rect(0, 0, *width, *height);
372 
373  Gdiplus::Status result = bitmap->LockBits(LOCKBITS_RECT_CAST(rect),
374  Gdiplus::ImageLockModeRead,
375  format, &bitmapdata);
376 
377  if (result != Gdiplus::Ok) {
378  if (result == Gdiplus::OutOfMemory)
380  else
382  bitmap->UnlockBits(&bitmapdata);
383  delete bitmap;
384  return NULL;
385  }
386 
387  unsigned char * src = (unsigned char *)bitmapdata.Scan0;
388 
389  unsigned char * dst = NULL;
390 
391  switch (format) {
392  case PixelFormat16bppGrayScale:
393  (*numcomponents) = 1;
394  dst = copy16bppGrayScaleBuffer(src, (*width), (*height),
395  bitmapdata.Stride);
396  break;
397  case PixelFormat24bppRGB:
398  (*numcomponents) = 3;
399  dst = copyImageBuffer(src, (*width), (*height),
400  (*numcomponents),
401  bitmapdata.Stride);
402  break;
403  case PixelFormat32bppARGB:
404  if (grayscale) {
405  (*numcomponents) = 2;
406  dst = copy32bppGrayScaleBuffer(src, (*width), (*height),
407  bitmapdata.Stride);
408  } else {
409  (*numcomponents) = 4;
410  dst = copyImageBuffer(src, (*width), (*height),
411  (*numcomponents),
412  bitmapdata.Stride);
413  }
414  break;
415  default:
416  dst = NULL;
417  }
418 
419  bitmap->UnlockBits(&bitmapdata);
420  delete bitmap;
421 
422  if (!dst) {
424  }
425 
426  return dst;
427 }
428 
429 char *
431 {
433  if (!gdiplus_init()) {
435  return NULL;
436  }
437 
438  UINT num = 0, size = 0;
439  Gdiplus::GetImageEncodersSize(&num, &size);
440 
441  Gdiplus::ImageCodecInfo * pImageCodecInfo = (Gdiplus::ImageCodecInfo *) malloc(size);
442 
443  Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
444 
445  char * formats = NULL;
446  size_t formats_size = 0;
447  size_t format_len = 0;
448 
449  for (unsigned int i = 0; i < num; i++) {
450  /* convert wide char to C string */
451  const wchar_t * w_format = pImageCodecInfo[i].FormatDescription;
452  size_t w_format_len = wcslen(w_format);
453  char * format = (char *)malloc(w_format_len+10);
454  wcstombs(format, w_format, w_format_len);
455  format[w_format_len] = '\0';
456  format_len = strlen(format);
457 
458  formats_size += format_len + ((i == 0) ? 1 : 2);
459  formats = (char *) realloc(formats, formats_size);
460 
461  /* make JPEG and TIFF strings 3 letter extensions */
462  /* FIXME: too dirty. wash it! 20060418 tamer. */
463  if (format_len == 4) {
464  if (!strncmp(format, "JPEG", 4)) {
465  format[2] = 'G'; format[3] = '\0';
466  } else if (!strncmp(format, "TIFF", 4)) {
467  format[3] = '\0';
468  }
469  }
470 
471  if (i == 0) {
472  strncpy(formats, format, format_len + 1);
473  } else {
474  strncat(formats, ",", 2);
475  strncat(formats, format, format_len + 1);
476  }
477 
478  free(format);
479  }
480 
481  free(pImageCodecInfo);
482 
483  return formats;
484 }
485 
486 static int
487 GetEncoderClsid(const char * format, CLSID * pClsid)
488 {
489  Gdiplus::ImageCodecInfo * pImageCodecInfo = NULL;
490  unsigned int num = 0, size = 0;
491 
492  Gdiplus::GetImageEncodersSize(&num, &size);
493  if (size == 0) { return -1; }
494 
495  pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
496  if (!pImageCodecInfo) { return -1; }
497 
498  Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
499 
500  /* convert C string filename to wide char */
501  wchar_t * format_wide = new wchar_t[strlen(format)+1];
502 
503  if (!format_wide) { return -1; }
504  mbstowcs(format_wide, format, strlen(format)+1);
505 
506  for (unsigned int i = 0; i < num; i++) {
507  if (wcsstr(pImageCodecInfo[i].MimeType, format_wide)) {
508  *pClsid = pImageCodecInfo[i].Clsid;
509  free(format_wide);
510  free(pImageCodecInfo);
511  return i;
512  }
513  }
514 
515  free(format_wide);
516  free(pImageCodecInfo);
517 
518  return -1;
519 }
520 
521 int
523  const unsigned char * bytes,
524  int width,
525  int height,
526  int numcomponents,
527  const char * filetypeext)
528 {
529  /* convert C string filename to wide char */
530  wchar_t * filename_wide = new wchar_t[strlen(filename)+1];
531 
532  if (!filename_wide) { gdipluserror = ERR_WRITE; return 0; }
533  mbstowcs(filename_wide, filename, strlen(filename)+1);
534 
535  Gdiplus::PixelFormat pixelFormat =
536  ((numcomponents == 1) || (numcomponents == 3)) ?
537  PixelFormat24bppRGB : PixelFormat32bppARGB;
538 
539  Gdiplus::Bitmap * bitmap = new Gdiplus::Bitmap(width, height, pixelFormat);
540  Gdiplus::BitmapData * bitmapData = new Gdiplus::BitmapData;
541  Gdiplus::Rect rect(0, 0, width, height);
542 
543  bitmap->LockBits(LOCKBITS_RECT_CAST(rect),
544  Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeWrite,
545  pixelFormat, bitmapData);
546 
547  unsigned char * dst = ((unsigned char *)bitmapData->Scan0);
548  const unsigned char * src = bytes + (width*height*numcomponents);
549 
550  int numcomp34 = ((numcomponents==1) || (numcomponents==3)) ? 3 : 4;
551  unsigned int stride = bitmapData->Stride - width*numcomp34;
552 
553  switch (numcomponents) {
554  case 1:
555  case 2:
556  {
557  /* FIXME: code for 2 components case has not been tested. comp 1
558  should rather be written out as PixelFormat8bppIndexed. comp 2?
559  20060420 tamer. */
560  for (int y = 0; y < height; y++) {
561  src -= width*numcomponents;
562  for (int x = 0; x < width*numcomponents; x+=numcomponents) {
563  *dst++ = src[x]; *dst++ = src[x]; *dst++ = src[x];
564  /* greyscale-alpha buffer */
565  if (numcomponents == 2) { *dst++ = src[x+1]; }
566  }
567  dst += stride;
568  }
569  }
570  break;
571  case 3:
572  case 4:
573  {
574  for (int y = 0; y < height; y++) {
575  src -= width*numcomponents;
576  for (int x = 0; x < width*numcomponents; x+=numcomponents) {
577  *dst++ = src[x+2]; *dst++ = src[x+1]; *dst++ = src[x];
578  /* ARGB GDI+ buffer, internally really represented as BGRA */
579  if (numcomponents == 4) { *dst++ = src[x+3]; }
580  }
581  dst += stride;
582  }
583  }
584  break;
585  default:
587  delete bitmapData;
588  delete bitmap;
589  delete filename_wide;
590  return 0;
591  }
592 
593  bitmap->UnlockBits(bitmapData);
594 
595  int ret;
596  CLSID imgClsid;
597 
598  /* handle special JPEG and TIFF naming cases */
599  /* FIXME: too dirty. wash it! 20060418 tamer. */
600  if (!strncmp(filetypeext, "jpg", 3)) {
601  ret = GetEncoderClsid("jpeg", &imgClsid);
602  } else if (!strncmp(filetypeext, "tif", 3)) {
603  ret = GetEncoderClsid("tiff", &imgClsid);
604  } else {
605  ret = GetEncoderClsid(filetypeext, &imgClsid);
606  }
607 
608  if (ret != -1) {
609  bitmap->Save(filename_wide, &imgClsid, NULL);
610  }
611 
612  delete bitmapData;
613  delete bitmap;
614  delete filename_wide;
615 
616  return 1;
617 }
618 
619 void *
621  int * width,
622  int * height,
623  int * numcomponents)
624 {
626 
627  if (!gdiplus_init()) { gdipluserror = ERR_INIT; return NULL; }
628 
629  /* convert C string filename to wide char */
630  wchar_t * filename_wide = new wchar_t[strlen(filename)+1];
631 
632  if (!filename_wide) { gdipluserror = ERR_OPEN; return NULL; }
633  mbstowcs(filename_wide, filename, strlen(filename)+1);
634 
635  Gdiplus::Bitmap * bitmap = new Gdiplus::Bitmap(filename_wide);
636 
637  if (bitmap && (bitmap->GetLastStatus() == Gdiplus::Ok)) {
638  *width = bitmap->GetWidth();
639  *height = bitmap->GetHeight();
640 
641  Gdiplus::PixelFormat pixelFormat = bitmap->GetPixelFormat();
642 
643  (*numcomponents) = (pixelFormat == PixelFormat8bppIndexed) ? 1 :
644  (pixelFormat == PixelFormat24bppRGB) ? 3 : 4;
645 
646  /* Simply use PixelFormat24bppRGB for GIF images, so GDI+ handles
647  them correctly and we don't have to write a verbose palette
648  handling essay to gain proper GIF support. FIXME: a bit
649  dirty. rinse it! 20060520 tamer. */
650  GUID guid; bitmap->GetRawFormat(&guid);
651  if (guid == Gdiplus::ImageFormatGIF) { (*numcomponents) = 4; }
652 
653  delete filename_wide;
654  return bitmap;
655  }
656 
657  if (bitmap) { delete bitmap; }
658  delete filename_wide;
659 
661  return NULL;
662 }
663 
664 void
665 simage_gdiplus_close(void * opendata)
666 {
667  Gdiplus::Bitmap * bitmap = (Gdiplus::Bitmap*)opendata;
668  if (bitmap) { delete bitmap; }
669 }
670 
671 int
672 simage_gdiplus_read_line(void * opendata, int y, unsigned char * buf)
673 {
674  Gdiplus::Bitmap * bitmap = (Gdiplus::Bitmap*)opendata;
675  if (!bitmap && !buf) { gdipluserror = ERR_READ; return 0; }
676 
677  Gdiplus::PixelFormat pixelFormat = bitmap->GetPixelFormat();
678 
679  int width = bitmap->GetWidth();
680  int height = bitmap->GetHeight();
681 
682  if (y < 0 || y > height) { gdipluserror = ERR_READ; return 0; }
683 
684  int numcomponents = (pixelFormat == PixelFormat8bppIndexed) ? 1 :
685  (pixelFormat == PixelFormat24bppRGB) ? 3 : 4;
686 
687  /* Simply use PixelFormat24bppRGB for GIF images, so GDI+ handles
688  them correctly and we don't have to write a verbose palette
689  handling essay to gain proper GIF support. FIXME: a bit
690  dirty. rinse it! 20060520 tamer. */
691  GUID guid; bitmap->GetRawFormat(&guid);
692  if (guid == Gdiplus::ImageFormatGIF) { numcomponents = 4; }
693 
694  /* default to RGBA conversion for other pixel formats */
695  if (numcomponents == 4) { pixelFormat = PixelFormat32bppARGB; }
696 
697  Gdiplus::BitmapData bitmapData;
698  Gdiplus::Rect rect(0, height-y-1, width, 1);
699 
700  bitmap->LockBits(LOCKBITS_RECT_CAST(rect), Gdiplus::ImageLockModeRead,
701  pixelFormat, &bitmapData);
702 
703  unsigned char * src = (unsigned char *)bitmapData.Scan0;
704 
705  switch (numcomponents) {
706  case 1:
707  memcpy(buf, src, width);
708  break;
709  case 3:
710  case 4:
711  {
712  for (int x = 0; x < width*numcomponents; x+=numcomponents) {
713  buf[x+2] = *src++; buf[x+1] = *src++; buf[x] = *src++;
714  /* ARGB GDI+ buffer, internally represented as BGRA */
715  if (numcomponents == 4) { buf[x+3] = *src++; }
716  }
717  }
718  break;
719  default:
721  return 0;
722  }
723 
724  bitmap->UnlockBits(&bitmapData);
725 
726  return 1;
727 }
728 
729 /*
730  * FIXME: unstable experimental region support API. Use it without
731  * knowing why and a fiercely looking fluffy cow will cross your way
732  * one day. The fluffy cow is gonna eat up all your code and will
733  * happily moo the doomsday symphony while blocking your getaway
734  * road. YA HAVE BEEN WARNED! 20060520 tamer.
735  */
736 int
738  int x, int y, int w, int h,
739  unsigned char * buf)
740 {
741  Gdiplus::Bitmap * bitmap = (Gdiplus::Bitmap*)opendata;
742  if (!bitmap && !buf) { gdipluserror = ERR_READ; return 0; }
743 
744  Gdiplus::PixelFormat pixelFormat = bitmap->GetPixelFormat();
745 
746  int width = bitmap->GetWidth();
747  int height = bitmap->GetHeight();
748 
749  if (x < 0 || y < 0 || w <= 0 || h <= 0 ||
750  (x+w > width) || (y+h > height)) { gdipluserror = ERR_READ; return 0; }
751 
752  int numcomponents = (pixelFormat == PixelFormat8bppIndexed) ? 1 :
753  (pixelFormat == PixelFormat24bppRGB) ? 3 : 4;
754 
755  /* Simply use PixelFormat24bppRGB for GIF images, so GDI+ handles
756  them correctly and we don't have to write a verbose palette
757  handling essay to gain proper GIF support. FIXME: a bit
758  dirty. rinse it! 20060520 tamer. */
759  GUID guid; bitmap->GetRawFormat(&guid);
760  if (guid == Gdiplus::ImageFormatGIF) { numcomponents = 4; }
761 
762  /* default to RGBA conversion for other pixel formats */
763  if (numcomponents == 4) { pixelFormat = PixelFormat32bppARGB; }
764 
765  Gdiplus::BitmapData bitmapData;
766  Gdiplus::Rect rect(x, y, w, h);
767 
768  bitmap->LockBits(LOCKBITS_RECT_CAST(rect), Gdiplus::ImageLockModeRead,
769  pixelFormat, &bitmapData);
770 
771  unsigned int stride = bitmapData.Stride - w*numcomponents;
772  unsigned char * src = (unsigned char *)bitmapData.Scan0;
773  unsigned char * dst = buf + w*h*numcomponents;
774 
775  switch (numcomponents) {
776  case 1:
777  {
778  for (int i = 0; i < h; i++) {
779  dst -= w;
780  memcpy(dst, src, w);
781  src += bitmapData.Stride;
782  }
783  }
784  break;
785  case 3:
786  case 4:
787  {
788  for (int i = 0; i < h; i++) {
789  dst -= w*numcomponents;
790  for (int j = 0; j < w*numcomponents; j+=numcomponents) {
791  dst[j+2] = *src++; dst[j+1] = *src++; dst[j] = *src++;
792  /* ARGB GDI+ buffer, internally really represented as BGRA */
793  if (numcomponents == 4) { dst[j+3] = *src++; }
794  }
795  src += stride;
796  }
797  }
798  break;
799  default:
801  return 0;
802  }
803 
804  bitmap->UnlockBits(&bitmapData);
805 
806  return 1;
807 }
#define LOCKBITS_RECT_CAST(arg)
char * filename
Definition: stream.c:38
static unsigned char * copy32bppGrayScaleBuffer(unsigned char *src, unsigned int width, unsigned int height, unsigned int stride)
static int gdiplus_init(void)
int simage_gdiplus_read_region(void *opendata, int x, int y, int w, int h, unsigned char *buf)
static Gdiplus::PixelFormat getReadFormat(Gdiplus::Bitmap &bitmap, bool &grayscale)
void simage_gdiplus_close(void *opendata)
static int gdipluserror
void * simage_gdiplus_open(const char *filename, int *width, int *height, int *numcomponents)
static unsigned char * copyImageBuffer(unsigned char *src, unsigned int width, unsigned int height, unsigned int numcomponents, unsigned int stride)
static unsigned char * copy16bppGrayScaleBuffer(unsigned char *src, unsigned int width, unsigned int height, unsigned int stride)
int simage_gdiplus_save(const char *filename, const unsigned char *bytes, int width, int height, int numcomponents, const char *filetypeext)
int simage_gdiplus_error(char *buffer, int buflen)
unsigned char * simage_gdiplus_load(const char *filename, int *width, int *height, int *numcomponents)
int simage_gdiplus_read_line(void *opendata, int y, unsigned char *buf)
int simage_gdiplus_identify(const char *ptr, const unsigned char *header, int headerlen)
char * simage_gdiplus_get_savers(void)
static int GetEncoderClsid(const char *format, CLSID *pClsid)