source: trunk/src/gdi32/dibitmap.cpp@ 7635

Last change on this file since 7635 was 7635, checked in by sandervl, 24 years ago

Added statistics for font, bitmap, pen, brush & region objects.

File size: 19.6 KB
Line 
1/* $Id: dibitmap.cpp,v 1.29 2001-12-15 18:50:25 sandervl Exp $ */
2
3/*
4 * GDI32 dib & bitmap code
5 *
6 * Copyright 1998 Sander van Leeuwen (sandervl@xs4all.nl)
7 * Copyright 1998 Patrick Haller
8 *
9 * Project Odin Software License can be found in LICENSE.TXT
10 *
11 */
12#include <os2win.h>
13#include <stdlib.h>
14#include <stdarg.h>
15#include <string.h>
16#include <misc.h>
17#include <cpuhlp.h>
18#include <winuser32.h>
19#include "dibsect.h"
20#include "rgbcvt.h"
21#include <stats.h>
22
23#define DBG_LOCALLOG DBG_dibitmap
24#include "dbglocal.h"
25
26ULONG CalcBitmapSize(ULONG cBits, LONG cx, LONG cy);
27
28//******************************************************************************
29//******************************************************************************
30HBITMAP WIN32API CreateDIBitmap(HDC hdc, const BITMAPINFOHEADER *lpbmih,
31 DWORD fdwInit, const void *lpbInit,
32 const BITMAPINFO *lpbmi, UINT fuUsage)
33{
34 int iHeight;
35 HBITMAP rc;
36 DWORD bitfields[3];
37 WORD *newbits = NULL;
38
39 //SvL: Completely wrong result when creating a 1bpp bitmap here (converted
40 // to 8bpp by Open32)
41 if(lpbmih->biBitCount == 1) {
42 dprintf(("WARNING: CreateDIBitmap doesn't handle 1bpp bitmaps very well!!!!!"));
43 }
44
45 //TEMPORARY HACK TO PREVENT CRASH IN OPEN32 (WSeB GA)
46
47 iHeight = lpbmih->biHeight;
48 if(lpbmih->biHeight < 0)
49 {
50 dprintf(("GDI32: CreateDIBitmap negative height! (%d,%d)", lpbmih->biWidth, lpbmih->biHeight));
51 //TODO: doesn't work if memory is readonly!!
52 ((BITMAPINFOHEADER *)lpbmih)->biHeight = -lpbmih->biHeight;
53
54 if(lpbInit && fdwInit == CBM_INIT) {
55 // upside down
56 HBITMAP rc = 0;
57 long lLineByte = DIB_GetDIBWidthBytes(lpbmih->biWidth, lpbmih->biBitCount);
58 long lHeight = lpbmih->biHeight;
59
60 newbits = (WORD *)malloc( lLineByte * lHeight );
61 if(newbits) {
62 unsigned char *pbSrc = (unsigned char *)lpbInit + lLineByte * (lHeight - 1);
63 unsigned char *pbDst = (unsigned char *)newbits;
64 for(int y = 0; y < lHeight; y++) {
65 memcpy( pbDst, pbSrc, lLineByte );
66 pbDst += lLineByte;
67 pbSrc -= lLineByte;
68 }
69 rc = CreateDIBitmap(hdc, lpbmih, fdwInit, newbits, lpbmi, fuUsage);
70 free( newbits );
71 }
72
73 ((BITMAPINFOHEADER *)lpbmih)->biHeight = iHeight;
74 return rc;
75 }
76 }
77
78 // 2000/09/01 PH Netscape 4.7
79 // If color depth of lpbhmi is 16 bit and lpbmi is 8 bit,
80 // Open32 will crash since it won't allocate any palette color memory,
81 // however wants to copy it later on ...
82 int biBitCount = lpbmih->biBitCount;
83
84 if (lpbmih->biBitCount != lpbmi->bmiHeader.biBitCount)
85 {
86 dprintf(("GDI32: CreateDIBitmap: color depths of bitmaps differ! (%d,%d\n", lpbmih->biBitCount,
87 lpbmi->bmiHeader.biBitCount));
88
89 ((BITMAPINFOHEADER *)lpbmih)->biBitCount = lpbmi->bmiHeader.biBitCount;
90 }
91
92 switch(lpbmih->biBitCount) {
93 case 15:
94 case 16: //Default if BI_BITFIELDS not set is RGB 555
95 bitfields[0] = (lpbmih->biCompression == BI_BITFIELDS) ? *(DWORD *)lpbmi->bmiColors : 0x7c00;
96 bitfields[1] = (lpbmih->biCompression == BI_BITFIELDS) ? *((DWORD *)lpbmi->bmiColors + 1) : 0x03e0;
97 bitfields[2] = (lpbmih->biCompression == BI_BITFIELDS) ? *((DWORD *)lpbmi->bmiColors + 2) : 0x001f;
98 break;
99 case 24:
100 case 32:
101 bitfields[0] = (lpbmih->biCompression == BI_BITFIELDS) ? *(DWORD *)lpbmi->bmiColors : 0xff0000;
102 bitfields[1] = (lpbmih->biCompression == BI_BITFIELDS) ? *((DWORD *)lpbmi->bmiColors + 1) : 0xff00;
103 bitfields[2] = (lpbmih->biCompression == BI_BITFIELDS) ? *((DWORD *)lpbmi->bmiColors + 2) : 0xff;
104 break;
105 default:
106 bitfields[0] = 0;
107 bitfields[1] = 0;
108 bitfields[2] = 0;
109 break;
110 }
111 if(bitfields[1] == 0x3E0 && lpbInit && fdwInit == CBM_INIT)
112 {//RGB 555?
113 dprintf(("RGB 555->565 conversion required %x %x %x", bitfields[0], bitfields[1], bitfields[2]));
114
115 int imgsize = CalcBitmapSize(lpbmih->biBitCount, lpbmih->biWidth, lpbmih->biHeight);
116
117 newbits = (WORD *)malloc(imgsize);
118 if(CPUFeatures & CPUID_MMX) {
119 RGB555to565MMX(newbits, (WORD *)lpbInit, imgsize/sizeof(WORD));
120 }
121 else RGB555to565(newbits, (WORD *)lpbInit, imgsize/sizeof(WORD));
122 lpbInit = newbits;
123 }
124
125 rc = O32_CreateDIBitmap(hdc, lpbmih, fdwInit, lpbInit, lpbmi, fuUsage);
126
127 dprintf(("GDI32: CreateDIBitmap %x %x %x %x %x returned %x (%d,%d, bps %d)", hdc, lpbmih, fdwInit, lpbInit, fuUsage, rc, lpbmih->biWidth, lpbmih->biHeight, lpbmih->biBitCount));
128
129 if(newbits) free(newbits);
130
131 ((BITMAPINFOHEADER *)lpbmih)->biHeight = iHeight;
132 ((BITMAPINFOHEADER *)lpbmih)->biBitCount = biBitCount;
133
134 if(rc) STATS_CreateDIBitmap(rc, hdc, lpbmih, fdwInit, lpbInit, lpbmi, fuUsage);
135
136 return rc;
137}
138//******************************************************************************
139//******************************************************************************
140HBITMAP WIN32API CreateCompatibleBitmap( HDC hdc, int nWidth, int nHeight)
141{
142 HBITMAP hBitmap;
143
144 hBitmap = O32_CreateCompatibleBitmap(hdc, nWidth, nHeight);
145 dprintf(("GDI32: CreateCompatibleBitmap %x (%d,%d) returned %x", hdc, nWidth, nHeight, hBitmap));
146 if(hBitmap) STATS_CreateCompatibleBitmap(hBitmap,hdc, nWidth, nHeight);
147
148 return hBitmap;
149}
150//******************************************************************************
151//CreateDisardableBitmap is obsolete and can be replaced by CreateCompatibleBitmap
152//******************************************************************************
153HBITMAP WIN32API CreateDiscardableBitmap(HDC hDC, int nWidth, int nHeight)
154{
155 dprintf(("GDI32: CreateDisardableBitmap\n"));
156 return CreateCompatibleBitmap(hDC, nWidth, nHeight);
157}
158//******************************************************************************
159//******************************************************************************
160HBITMAP WIN32API CreateBitmap(int nWidth, int nHeight, UINT cPlanes,
161 UINT cBitsPerPel, const void *lpvBits)
162{
163 HBITMAP hBitmap;
164
165 hBitmap = O32_CreateBitmap(nWidth, nHeight, cPlanes, cBitsPerPel, lpvBits);
166 dprintf(("GDI32: CreateBitmap (%d,%d) bps %d returned %x", nWidth, nHeight, cBitsPerPel, hBitmap));
167 if(hBitmap) STATS_CreateBitmap(hBitmap,nWidth, nHeight, cPlanes, cBitsPerPel, lpvBits);
168
169 return(hBitmap);
170}
171//******************************************************************************
172//******************************************************************************
173HBITMAP WIN32API CreateBitmapIndirect( const BITMAP *pBitmap)
174{
175 HBITMAP hBitmap;
176
177 dprintf(("GDI32: CreateBitmapIndirect (%d,%d) bpp %d bits %x", pBitmap->bmWidth, pBitmap->bmHeight, pBitmap->bmBitsPixel, pBitmap->bmBits));
178 hBitmap = O32_CreateBitmapIndirect(pBitmap);
179 if(hBitmap) STATS_CreateBitmapIndirect(hBitmap, pBitmap);
180
181 dprintf(("GDI32: CreateBitmapIndirect returned %x", hBitmap));
182 return hBitmap;
183}
184//******************************************************************************
185//*********************************************************************************
186HBITMAP WIN32API CreateDIBSection( HDC hdc, BITMAPINFO *pbmi, UINT iUsage,
187 VOID **ppvBits, HANDLE hSection, DWORD dwOffset)
188{
189 HBITMAP res = 0;
190 BOOL fFlip = 0;
191 int iHeight, iWidth;
192 BOOL fCreateDC = FALSE;
193
194 dprintf(("GDI32: CreateDIBSection %x %x %x %x %x %d", hdc, pbmi, iUsage, ppvBits, hSection, dwOffset));
195
196 //SvL: 13-9-98: StarCraft uses bitmap with negative height
197 iWidth = pbmi->bmiHeader.biWidth;
198 if(pbmi->bmiHeader.biWidth < 0)
199 {
200 dprintf(("CreateDIBSection: width %d", pbmi->bmiHeader.biWidth));
201 pbmi->bmiHeader.biWidth = -pbmi->bmiHeader.biWidth;
202 fFlip = FLIP_HOR;
203 }
204 iHeight = pbmi->bmiHeader.biHeight;
205 if(pbmi->bmiHeader.biHeight < 0)
206 {
207 dprintf(("CreateDIBSection: height %d", pbmi->bmiHeader.biHeight));
208 pbmi->bmiHeader.biHeight = -pbmi->bmiHeader.biHeight;
209 fFlip |= FLIP_VERT;
210 }
211
212 //SvL: RP7 (update) calls this api with hdc == 0
213 if(hdc == 0) {
214 hdc = CreateCompatibleDC(0);
215 fCreateDC = TRUE;
216 }
217 res = O32_CreateDIBitmap(hdc, &pbmi->bmiHeader, 0, NULL, pbmi, iUsage);
218 if (res)
219 {
220 char PalSize;
221 DIBSection *dsect;
222
223 dsect = new DIBSection((BITMAPINFOHEADER_W *)&pbmi->bmiHeader, (char *)&pbmi->bmiColors, iUsage, hSection, dwOffset, (DWORD)res, fFlip);
224
225 if(dsect != NULL)
226 {
227 PalSize = dsect->GetBitCount();
228 if(PalSize <= 8)
229 {
230 ULONG Pal[256], nrcolors;
231 LOGPALETTE tmpPal = { 0x300,1,{0,0,0,0}};
232 HPALETTE hpalCur, hpalTmp;
233
234 // Now get the current Palette from the DC
235 hpalTmp = CreatePalette(&tmpPal);
236 hpalCur = SelectPalette(hdc, hpalTmp, FALSE);
237
238 // and use it to set the DIBColorTable
239 nrcolors = GetPaletteEntries( hpalCur, 0, 1<<PalSize, (LPPALETTEENTRY)&Pal);
240 dsect->SetDIBColorTable(0, nrcolors, (LPPALETTEENTRY)&Pal);
241
242 // Restore the DC Palette
243 SelectPalette(hdc,hpalCur,FALSE);
244 DeleteObject(hpalTmp);
245 }
246
247 if(ppvBits!=NULL)
248 *ppvBits = dsect->GetDIBObject();
249
250 pbmi->bmiHeader.biWidth = iWidth;
251 pbmi->bmiHeader.biHeight = iHeight;
252
253 if(fCreateDC) DeleteDC(hdc);
254
255 STATS_CreateDIBSection(res, hdc, pbmi, iUsage, ppvBits, hSection, dwOffset);
256
257 return(res);
258 }
259 }
260 if(fCreateDC) DeleteDC(hdc);
261
262 /* Error. */
263 if (res)
264 DeleteObject(res);
265 *ppvBits = NULL;
266
267#ifdef DEBUG
268 dprintf(("GDI32: CreateDIBSection, error!\n"));
269 dprintf(("pbmi->biWidth %d", pbmi->bmiHeader.biWidth));
270 dprintf(("pbmi->biHeight %d", pbmi->bmiHeader.biHeight));
271 dprintf(("pbmi->biBitCount %d", pbmi->bmiHeader.biBitCount));
272#endif
273
274 return 0;
275}
276//******************************************************************************
277//******************************************************************************
278UINT WIN32API GetDIBColorTable(HDC hdc, UINT uStartIndex, UINT cEntries,
279 RGBQUAD *pColors)
280{
281 DIBSection *dsect = DIBSection::findHDC(hdc);
282 UINT rc;
283 int i;
284
285 dprintf(("GetDIBColorTable %x %d->%d %x", hdc, uStartIndex, cEntries, pColors));
286
287 if(dsect)
288 {
289 return(dsect->GetDIBColorTable(uStartIndex, cEntries, pColors));
290 }
291 //TODO: Is this correct?????
292 // Wine returns 0 if bitmap selected into dc with bpp > 8
293 HPALETTE hpal = GetCurrentObject(hdc, OBJ_PAL);
294 rc = O32_GetPaletteEntries(hpal, uStartIndex, cEntries, (PALETTEENTRY *)pColors);
295 for(i=0;i<cEntries;i++)
296 {
297 BYTE tmp;
298 tmp = pColors[i].rgbBlue;
299 pColors[i].rgbBlue = pColors[i].rgbRed;
300 pColors[i].rgbRed = tmp;
301 pColors[i].rgbReserved = 0;
302 }
303 dprintf(("GDI32: GetDIBColorTable returns %d\n", rc));
304 return(rc);
305}
306//******************************************************************************
307//******************************************************************************
308UINT WIN32API SetDIBColorTable(HDC hdc, UINT uStartIndex, UINT cEntries,
309 RGBQUAD *pColors)
310{
311 DIBSection *dsect = DIBSection::findHDC(hdc);
312
313 dprintf(("GDI32: SetDIBColorTable %x %d,%d %x", hdc, uStartIndex, cEntries, pColors));
314 if(dsect)
315 {
316 return(dsect->SetDIBColorTable(uStartIndex, cEntries, pColors));
317 }
318 else return(0);
319}
320//******************************************************************************
321//******************************************************************************
322LONG WIN32API GetBitmapBits( HBITMAP hBitmap, LONG arg2, PVOID arg3)
323{
324 dprintf(("GDI32: GetBitmapBits %x", hBitmap));
325 return O32_GetBitmapBits(hBitmap, arg2, arg3);
326}
327//******************************************************************************
328//******************************************************************************
329LONG WIN32API SetBitmapBits( HBITMAP hBitmap, LONG arg2, const VOID * arg3)
330{
331 dprintf(("GDI32: SetBitmapBits %x", hBitmap));
332 return O32_SetBitmapBits(hBitmap, (DWORD)arg2, arg3);
333}
334//******************************************************************************
335//******************************************************************************
336BOOL WIN32API GetBitmapDimensionEx( HBITMAP hBitmap, PSIZE pSize)
337{
338 dprintf(("GDI32: GetBitmapDimensionEx %x (%d,%d)", hBitmap, pSize->cx, pSize->cy));
339 return O32_GetBitmapDimensionEx(hBitmap, pSize);
340}
341//******************************************************************************
342//******************************************************************************
343BOOL WIN32API SetBitmapDimensionEx( HBITMAP arg1, int arg2, int arg3, PSIZE arg4)
344{
345 dprintf(("GDI32: SetBitmapDimensionEx"));
346 return O32_SetBitmapDimensionEx(arg1, arg2, arg3, arg4);
347}
348//******************************************************************************
349//******************************************************************************
350int WIN32API GetDIBits(HDC hdc, HBITMAP hBitmap, UINT uStartScan, UINT cScanLines,
351 void *lpvBits, PBITMAPINFO lpbi, UINT uUsage)
352{
353 int nrlines;
354
355 dprintf(("GDI32: GetDIBits %x %x %d %d %x %x (biBitCount %d) %d", hdc, hBitmap, uStartScan, cScanLines, lpvBits, lpbi, lpbi->bmiHeader.biBitCount, uUsage));
356
357 //SvL: WGSS screws up the DC if it's a memory DC
358 // TODO: Fix in WGSS
359 HDC hdcMem = CreateCompatibleDC(0);
360
361 nrlines = O32_GetDIBits(hdcMem, hBitmap, uStartScan, cScanLines, lpvBits, lpbi, uUsage);
362
363 DeleteDC(hdcMem);
364
365 if(lpvBits) {
366 // set proper color masks (only if lpvBits not NULL)
367 switch(lpbi->bmiHeader.biBitCount) {
368 case 15:
369 case 16: //RGB 565
370 ((DWORD*)(lpbi->bmiColors))[0] = 0x7c00;
371 ((DWORD*)(lpbi->bmiColors))[1] = 0x03E0;
372 ((DWORD*)(lpbi->bmiColors))[2] = 0x001F;
373 break;
374 case 24:
375 case 32:
376 ((DWORD*)(lpbi->bmiColors))[0] = 0xFF0000;
377 ((DWORD*)(lpbi->bmiColors))[1] = 0x00FF00;
378 ((DWORD*)(lpbi->bmiColors))[2] = 0x0000FF;
379 break;
380 }
381 }
382 if(nrlines && lpvBits && lpbi->bmiHeader.biBitCount == 16 && ((DWORD*)(lpbi->bmiColors))[1] == 0x3E0)
383 {//RGB 555?
384 dprintf(("RGB 565->555 conversion required"));
385
386 int imgsize = CalcBitmapSize(lpbi->bmiHeader.biBitCount,
387 lpbi->bmiHeader.biWidth, nrlines);
388
389 if(CPUFeatures & CPUID_MMX) {
390 RGB565to555MMX((WORD *)lpvBits, (WORD *)lpvBits, imgsize/sizeof(WORD));
391 }
392 else RGB565to555((WORD *)lpvBits, (WORD *)lpvBits, imgsize/sizeof(WORD));
393 }
394
395 //WGSS/Open32 returns 0 when querying the bitmap info; must return nr of scanlines
396 //as 0 signals failure
397 if(lpvBits == NULL) {
398 nrlines = cScanLines;
399 }
400 dprintf(("GDI32: GetDIBits returned %d", nrlines));
401 return nrlines;
402}
403//******************************************************************************
404//******************************************************************************
405void WIN32API ConvertRGB555to565(LPVOID dest, LPVOID src, UINT imgsize)
406{
407 if(CPUFeatures & CPUID_MMX) {
408 RGB555to565MMX((WORD *)dest, (WORD *)src, imgsize/sizeof(WORD));
409 }
410 else RGB555to565((WORD *)dest, (WORD *)src, imgsize/sizeof(WORD));
411}
412//******************************************************************************
413//******************************************************************************
414int WIN32API SetDIBits(HDC hdc, HBITMAP hBitmap, UINT startscan, UINT numlines, const VOID *pBits,
415 const BITMAPINFO *pBitmapInfo, UINT usage)
416{
417 int ret;
418 DWORD bitfields[3];
419 WORD *newbits = NULL;
420
421 dprintf(("GDI32: SetDIBits %x %x %x %x %x %x %x", hdc, hBitmap, startscan, numlines, pBits, pBitmapInfo, usage));
422
423 //SvL: Open32's SetDIBits really messes things up for 1 bpp bitmaps, must use SetBitmapBits
424 if(pBitmapInfo->bmiHeader.biBitCount == 1 && startscan == 0 && numlines == pBitmapInfo->bmiHeader.biHeight)
425 {//WARNING: hack alert!
426 int dibwidth = DIB_GetDIBWidthBytes(pBitmapInfo->bmiHeader.biWidth, 1);
427 int bmpwidth = BITMAP_GetWidthBytes(pBitmapInfo->bmiHeader.biWidth, 1);
428 char *newpix = (char *)malloc(dibwidth*pBitmapInfo->bmiHeader.biHeight);
429 char *orgpix = (char *)pBits;
430 int ret;
431
432 dprintf(("Flipping 1bpp bitmap and calling SetBitmapBits (WORKAROUND) (%d -> %d)", dibwidth, bmpwidth));
433 newpix += ((pBitmapInfo->bmiHeader.biHeight-1)*bmpwidth);
434
435 //flip bitmap here; SetDIBits assumes origin is left bottom, SetBitmapBits left top
436 //SetDIBits assumes DWORD aligned data
437 //SetBitmapBits assumes WORD aligned data
438 for(int i=0;i<pBitmapInfo->bmiHeader.biHeight;i++) {
439 memcpy(newpix, orgpix, bmpwidth);
440
441 newpix -= bmpwidth;
442 orgpix += dibwidth;
443 }
444 newpix += bmpwidth;
445 ret = O32_SetBitmapBits(hBitmap, pBitmapInfo->bmiHeader.biSizeImage, newpix);
446
447 free(newpix);
448 return ret;
449 }
450#ifdef DEBUG
451 if(pBitmapInfo->bmiHeader.biBitCount == 1) {
452 dprintf(("ERROR: SetDIBits does NOT work well for 1 bpp bitmaps!!!!!"));
453 }
454#endif
455
456 switch(pBitmapInfo->bmiHeader.biBitCount) {
457 case 15:
458 case 16: //Default if BI_BITFIELDS not set is RGB 555
459 bitfields[0] = (pBitmapInfo->bmiHeader.biCompression == BI_BITFIELDS) ? *(DWORD *)pBitmapInfo->bmiColors : 0x7c00;
460 bitfields[1] = (pBitmapInfo->bmiHeader.biCompression == BI_BITFIELDS) ? *((DWORD *)pBitmapInfo->bmiColors + 1) : 0x03e0;
461 bitfields[2] = (pBitmapInfo->bmiHeader.biCompression == BI_BITFIELDS) ? *((DWORD *)pBitmapInfo->bmiColors + 2) : 0x001f;
462 break;
463
464 case 24:
465 case 32:
466 bitfields[0] = (pBitmapInfo->bmiHeader.biCompression == BI_BITFIELDS) ? *(DWORD *)pBitmapInfo->bmiColors : 0xff0000;
467 bitfields[1] = (pBitmapInfo->bmiHeader.biCompression == BI_BITFIELDS) ? *((DWORD *)pBitmapInfo->bmiColors + 1) : 0xff00;
468 bitfields[2] = (pBitmapInfo->bmiHeader.biCompression == BI_BITFIELDS) ? *((DWORD *)pBitmapInfo->bmiColors + 2) : 0xff;
469 break;
470 default:
471 bitfields[0] = 0;
472 bitfields[1] = 0;
473 bitfields[2] = 0;
474 break;
475 }
476 if(pBits && bitfields[1] == 0x3E0)
477 {//RGB 555?
478 dprintf(("RGB 555->565 conversion required %x %x %x", bitfields[0], bitfields[1], bitfields[2]));
479
480 int imgsize = CalcBitmapSize(pBitmapInfo->bmiHeader.biBitCount,
481 pBitmapInfo->bmiHeader.biWidth, numlines);
482
483 newbits = (WORD *)malloc(imgsize);
484 if(CPUFeatures & CPUID_MMX) {
485 RGB555to565MMX(newbits, (WORD *)pBits, imgsize/sizeof(WORD));
486 }
487 else RGB555to565(newbits, (WORD *)pBits, imgsize/sizeof(WORD));
488 pBits = newbits;
489 }
490
491 ret = O32_SetDIBits(hdc, hBitmap, startscan, numlines, pBits, pBitmapInfo, usage);
492 if(newbits) free(newbits);
493
494 if(DIBSection::getSection() != NULL)
495 {
496 DIBSection *dsect;
497
498 dsect = DIBSection::findObj(hBitmap);
499 if(dsect) {
500 HBITMAP hBmpOld = SelectObject(hdc, hBitmap);
501 dsect->sync(hdc, 0, dsect->GetHeight());
502 SelectObject(hdc, hBmpOld);
503 }
504 }
505
506 return ret;
507}
508//******************************************************************************
509//******************************************************************************
Note: See TracBrowser for help on using the repository browser.