]> code.delx.au - refind/blob - libeg/image.c
Added PNG support for icons, selection images, & banners.
[refind] / libeg / image.c
1 /*
2 * libeg/image.c
3 * Image handling functions
4 *
5 * Copyright (c) 2006 Christoph Pfisterer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 #include "libegint.h"
38 #include "../refind/global.h"
39 #include "../refind/lib.h"
40 #include "../refind/screen.h"
41 #include "../include/refit_call_wrapper.h"
42 #include "lodepng.h"
43
44 #define MAX_FILE_SIZE (1024*1024*1024)
45
46 #ifndef __MAKEWITH_GNUEFI
47 #define LibLocateHandle gBS->LocateHandleBuffer
48 #define LibOpenRoot EfiLibOpenRoot
49 #endif
50
51 //
52 // Basic image handling
53 //
54
55 EG_IMAGE * egCreateImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha)
56 {
57 EG_IMAGE *NewImage;
58
59 NewImage = (EG_IMAGE *) AllocatePool(sizeof(EG_IMAGE));
60 if (NewImage == NULL)
61 return NULL;
62 NewImage->PixelData = (EG_PIXEL *) AllocatePool(Width * Height * sizeof(EG_PIXEL));
63 if (NewImage->PixelData == NULL) {
64 FreePool(NewImage);
65 return NULL;
66 }
67
68 NewImage->Width = Width;
69 NewImage->Height = Height;
70 NewImage->HasAlpha = HasAlpha;
71 return NewImage;
72 }
73
74 EG_IMAGE * egCreateFilledImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha, IN EG_PIXEL *Color)
75 {
76 EG_IMAGE *NewImage;
77
78 NewImage = egCreateImage(Width, Height, HasAlpha);
79 if (NewImage == NULL)
80 return NULL;
81
82 egFillImage(NewImage, Color);
83 return NewImage;
84 }
85
86 EG_IMAGE * egCopyImage(IN EG_IMAGE *Image)
87 {
88 EG_IMAGE *NewImage;
89
90 NewImage = egCreateImage(Image->Width, Image->Height, Image->HasAlpha);
91 if (NewImage == NULL)
92 return NULL;
93
94 CopyMem(NewImage->PixelData, Image->PixelData, Image->Width * Image->Height * sizeof(EG_PIXEL));
95 return NewImage;
96 }
97
98 // Returns a smaller image composed of the specified crop area from the larger area.
99 // If the specified area is larger than is in the original, returns NULL.
100 EG_IMAGE * egCropImage(IN EG_IMAGE *Image, IN UINTN StartX, IN UINTN StartY, IN UINTN Width, IN UINTN Height) {
101 EG_IMAGE *NewImage = NULL;
102 UINTN x, y;
103
104 if (((StartX + Width) > Image->Width) || ((StartY + Height) > Image->Height))
105 return NULL;
106
107 NewImage = egCreateImage(Width, Height, Image->HasAlpha);
108 if (NewImage == NULL)
109 return NULL;
110
111 for (y = 0; y < Height; y++) {
112 for (x = 0; x < Width; x++) {
113 NewImage->PixelData[y * NewImage->Width + x] = Image->PixelData[(y + StartY) * Image->Width + x + StartX];
114 }
115 }
116 return NewImage;
117 } // EG_IMAGE * egCropImage()
118
119 VOID egFreeImage(IN EG_IMAGE *Image)
120 {
121 if (Image != NULL) {
122 if (Image->PixelData != NULL)
123 FreePool(Image->PixelData);
124 FreePool(Image);
125 }
126 }
127
128 //
129 // Basic file operations
130 //
131
132 EFI_STATUS egLoadFile(IN EFI_FILE* BaseDir, IN CHAR16 *FileName,
133 OUT UINT8 **FileData, OUT UINTN *FileDataLength)
134 {
135 EFI_STATUS Status;
136 EFI_FILE_HANDLE FileHandle;
137 EFI_FILE_INFO *FileInfo;
138 UINT64 ReadSize;
139 UINTN BufferSize;
140 UINT8 *Buffer;
141
142 Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
143 if (EFI_ERROR(Status)) {
144 return Status;
145 }
146
147 FileInfo = LibFileInfo(FileHandle);
148 if (FileInfo == NULL) {
149 refit_call1_wrapper(FileHandle->Close, FileHandle);
150 return EFI_NOT_FOUND;
151 }
152 ReadSize = FileInfo->FileSize;
153 if (ReadSize > MAX_FILE_SIZE)
154 ReadSize = MAX_FILE_SIZE;
155 FreePool(FileInfo);
156
157 BufferSize = (UINTN)ReadSize; // was limited to 1 GB above, so this is safe
158 Buffer = (UINT8 *) AllocatePool(BufferSize);
159 if (Buffer == NULL) {
160 refit_call1_wrapper(FileHandle->Close, FileHandle);
161 return EFI_OUT_OF_RESOURCES;
162 }
163
164 Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &BufferSize, Buffer);
165 refit_call1_wrapper(FileHandle->Close, FileHandle);
166 if (EFI_ERROR(Status)) {
167 FreePool(Buffer);
168 return Status;
169 }
170
171 *FileData = Buffer;
172 *FileDataLength = BufferSize;
173 return EFI_SUCCESS;
174 }
175
176 static EFI_GUID ESPGuid = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
177
178 static EFI_STATUS egFindESP(OUT EFI_FILE_HANDLE *RootDir)
179 {
180 EFI_STATUS Status;
181 UINTN HandleCount = 0;
182 EFI_HANDLE *Handles;
183
184 Status = LibLocateHandle(ByProtocol, &ESPGuid, NULL, &HandleCount, &Handles);
185 if (!EFI_ERROR(Status) && HandleCount > 0) {
186 *RootDir = LibOpenRoot(Handles[0]);
187 if (*RootDir == NULL)
188 Status = EFI_NOT_FOUND;
189 FreePool(Handles);
190 }
191 return Status;
192 }
193
194 EFI_STATUS egSaveFile(IN EFI_FILE* BaseDir OPTIONAL, IN CHAR16 *FileName,
195 IN UINT8 *FileData, IN UINTN FileDataLength)
196 {
197 EFI_STATUS Status;
198 EFI_FILE_HANDLE FileHandle;
199 UINTN BufferSize;
200
201 if (BaseDir == NULL) {
202 Status = egFindESP(&BaseDir);
203 if (EFI_ERROR(Status))
204 return Status;
205 }
206
207 Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName,
208 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
209 if (EFI_ERROR(Status))
210 return Status;
211
212 BufferSize = FileDataLength;
213 Status = refit_call3_wrapper(FileHandle->Write, FileHandle, &BufferSize, FileData);
214 refit_call1_wrapper(FileHandle->Close, FileHandle);
215
216 return Status;
217 }
218
219 //
220 // Loading images from files and embedded data
221 //
222
223 static CHAR16 * egFindExtension(IN CHAR16 *FileName)
224 {
225 UINTN i;
226
227 for (i = StrLen(FileName); i >= 0; i--) {
228 if (FileName[i] == '.')
229 return FileName + i + 1;
230 if (FileName[i] == '/' || FileName[i] == '\\')
231 break;
232 }
233 return FileName + StrLen(FileName);
234 }
235
236 static EG_IMAGE * egDecodeAny(IN UINT8 *FileData, IN UINTN FileDataLength,
237 IN CHAR16 *Format, IN UINTN IconSize, IN BOOLEAN WantAlpha)
238 {
239 EG_IMAGE *NewImage = NULL;
240
241 // Note: The UEFI implementation in Gigabyte's Hybrid EFI is buggy and does
242 // a case-sensitive comparison in StriCmp rather than the case-insensitive
243 // comparison that the spec says should be done. As a workaround, we repeat
244 // the comparison twice here.
245 // dispatch by extension
246 if ((StriCmp(Format, L"BMP") == 0) || (StriCmp(Format, L"bmp") == 0)) {
247 NewImage = egDecodeBMP(FileData, FileDataLength, IconSize, WantAlpha);
248 } else if ((StriCmp(Format, L"ICNS") == 0) || (StriCmp(Format, L"icns") == 0)) {
249 NewImage = egDecodeICNS(FileData, FileDataLength, IconSize, WantAlpha);
250 } else if ((StriCmp(Format, L"PNG") == 0) || (StriCmp(Format, L"png") == 0)) {
251 NewImage = egDecodePNG(FileData, FileDataLength, IconSize, WantAlpha);
252 } // if/else
253
254 return NewImage;
255 }
256
257 EG_IMAGE * egLoadImage(IN EFI_FILE* BaseDir, IN CHAR16 *FileName, IN BOOLEAN WantAlpha)
258 {
259 EFI_STATUS Status;
260 UINT8 *FileData;
261 UINTN FileDataLength;
262 EG_IMAGE *NewImage;
263
264 if (BaseDir == NULL || FileName == NULL)
265 return NULL;
266
267 // load file
268 Status = egLoadFile(BaseDir, FileName, &FileData, &FileDataLength);
269 if (EFI_ERROR(Status))
270 return NULL;
271
272 // decode it
273 NewImage = egDecodeAny(FileData, FileDataLength, egFindExtension(FileName), 128, WantAlpha);
274 FreePool(FileData);
275
276 return NewImage;
277 }
278
279 // Load an icon from (BaseDir)/Path, extracting the icon of size IconSize x IconSize.
280 // If the initial attempt is unsuccessful, try again, replacing the directory
281 // component of Path with DEFAULT_ICONS_DIR.
282 // Note: The assumption is that BaseDir points to rEFInd's home directory and Path
283 // includes one subdirectory level. If this changes in future revisions, it may be
284 // necessary to alter the code that tries again with DEFAULT_ICONS_DIR.
285 // Returns a pointer to the image data, or NULL if the icon could not be loaded.
286 EG_IMAGE * egLoadIcon(IN EFI_FILE* BaseDir, IN CHAR16 *Path, IN UINTN IconSize)
287 {
288 EFI_STATUS Status;
289 UINT8 *FileData;
290 UINTN FileDataLength;
291 CHAR16 *FileName, FileName2[256];
292 EG_IMAGE *NewImage;
293
294 if (BaseDir == NULL || Path == NULL)
295 return NULL;
296
297 // load file
298 Status = egLoadFile(BaseDir, Path, &FileData, &FileDataLength);
299 if (EFI_ERROR(Status)) {
300 FileName = Basename(Path); // Note: FileName is a pointer within Path; DON'T FREE IT!
301 SPrint(FileName2, 255, L"%s\\%s", DEFAULT_ICONS_DIR, FileName);
302 Status = egLoadFile(BaseDir, FileName2, &FileData, &FileDataLength);
303 if (EFI_ERROR(Status))
304 return NULL;
305 }
306
307 // decode it
308 NewImage = egDecodeAny(FileData, FileDataLength, egFindExtension(Path), IconSize, TRUE);
309 FreePool(FileData);
310 if ((NewImage->Width != IconSize) || (NewImage->Height != IconSize)) {
311 Print(L"Warning: Attempt to load icon of the wrong size from '%s'\n", Path);
312 MyFreePool(NewImage);
313 NewImage = NULL;
314 }
315
316 return NewImage;
317 } // EG_IMAGE *egLoadIcon()
318
319 EG_IMAGE * egDecodeImage(IN UINT8 *FileData, IN UINTN FileDataLength, IN CHAR16 *Format, IN BOOLEAN WantAlpha)
320 {
321 return egDecodeAny(FileData, FileDataLength, Format, 128, WantAlpha);
322 }
323
324 EG_IMAGE * egPrepareEmbeddedImage(IN EG_EMBEDDED_IMAGE *EmbeddedImage, IN BOOLEAN WantAlpha)
325 {
326 EG_IMAGE *NewImage;
327 UINT8 *CompData;
328 UINTN CompLen;
329 UINTN PixelCount;
330
331 // sanity check
332 if (EmbeddedImage->PixelMode > EG_MAX_EIPIXELMODE ||
333 (EmbeddedImage->CompressMode != EG_EICOMPMODE_NONE && EmbeddedImage->CompressMode != EG_EICOMPMODE_RLE))
334 return NULL;
335
336 // allocate image structure and pixel buffer
337 NewImage = egCreateImage(EmbeddedImage->Width, EmbeddedImage->Height, WantAlpha);
338 if (NewImage == NULL)
339 return NULL;
340
341 CompData = (UINT8 *)EmbeddedImage->Data; // drop const
342 CompLen = EmbeddedImage->DataLength;
343 PixelCount = EmbeddedImage->Width * EmbeddedImage->Height;
344
345 // FUTURE: for EG_EICOMPMODE_EFICOMPRESS, decompress whole data block here
346
347 if (EmbeddedImage->PixelMode == EG_EIPIXELMODE_GRAY ||
348 EmbeddedImage->PixelMode == EG_EIPIXELMODE_GRAY_ALPHA) {
349
350 // copy grayscale plane and expand
351 if (EmbeddedImage->CompressMode == EG_EICOMPMODE_RLE) {
352 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, r), PixelCount);
353 } else {
354 egInsertPlane(CompData, PLPTR(NewImage, r), PixelCount);
355 CompData += PixelCount;
356 }
357 egCopyPlane(PLPTR(NewImage, r), PLPTR(NewImage, g), PixelCount);
358 egCopyPlane(PLPTR(NewImage, r), PLPTR(NewImage, b), PixelCount);
359
360 } else if (EmbeddedImage->PixelMode == EG_EIPIXELMODE_COLOR ||
361 EmbeddedImage->PixelMode == EG_EIPIXELMODE_COLOR_ALPHA) {
362
363 // copy color planes
364 if (EmbeddedImage->CompressMode == EG_EICOMPMODE_RLE) {
365 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, r), PixelCount);
366 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, g), PixelCount);
367 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, b), PixelCount);
368 } else {
369 egInsertPlane(CompData, PLPTR(NewImage, r), PixelCount);
370 CompData += PixelCount;
371 egInsertPlane(CompData, PLPTR(NewImage, g), PixelCount);
372 CompData += PixelCount;
373 egInsertPlane(CompData, PLPTR(NewImage, b), PixelCount);
374 CompData += PixelCount;
375 }
376
377 } else {
378
379 // set color planes to black
380 egSetPlane(PLPTR(NewImage, r), 0, PixelCount);
381 egSetPlane(PLPTR(NewImage, g), 0, PixelCount);
382 egSetPlane(PLPTR(NewImage, b), 0, PixelCount);
383
384 }
385
386 if (WantAlpha && (EmbeddedImage->PixelMode == EG_EIPIXELMODE_GRAY_ALPHA ||
387 EmbeddedImage->PixelMode == EG_EIPIXELMODE_COLOR_ALPHA ||
388 EmbeddedImage->PixelMode == EG_EIPIXELMODE_ALPHA)) {
389
390 // copy alpha plane
391 if (EmbeddedImage->CompressMode == EG_EICOMPMODE_RLE) {
392 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, a), PixelCount);
393 } else {
394 egInsertPlane(CompData, PLPTR(NewImage, a), PixelCount);
395 CompData += PixelCount;
396 }
397
398 } else {
399 egSetPlane(PLPTR(NewImage, a), WantAlpha ? 255 : 0, PixelCount);
400 }
401
402 return NewImage;
403 }
404
405 //
406 // Compositing
407 //
408
409 VOID egRestrictImageArea(IN EG_IMAGE *Image,
410 IN UINTN AreaPosX, IN UINTN AreaPosY,
411 IN OUT UINTN *AreaWidth, IN OUT UINTN *AreaHeight)
412 {
413 if (AreaPosX >= Image->Width || AreaPosY >= Image->Height) {
414 // out of bounds, operation has no effect
415 *AreaWidth = 0;
416 *AreaHeight = 0;
417 } else {
418 // calculate affected area
419 if (*AreaWidth > Image->Width - AreaPosX)
420 *AreaWidth = Image->Width - AreaPosX;
421 if (*AreaHeight > Image->Height - AreaPosY)
422 *AreaHeight = Image->Height - AreaPosY;
423 }
424 }
425
426 VOID egFillImage(IN OUT EG_IMAGE *CompImage, IN EG_PIXEL *Color)
427 {
428 UINTN i;
429 EG_PIXEL FillColor;
430 EG_PIXEL *PixelPtr;
431
432 FillColor = *Color;
433 if (!CompImage->HasAlpha)
434 FillColor.a = 0;
435
436 PixelPtr = CompImage->PixelData;
437 for (i = 0; i < CompImage->Width * CompImage->Height; i++, PixelPtr++)
438 *PixelPtr = FillColor;
439 }
440
441 VOID egFillImageArea(IN OUT EG_IMAGE *CompImage,
442 IN UINTN AreaPosX, IN UINTN AreaPosY,
443 IN UINTN AreaWidth, IN UINTN AreaHeight,
444 IN EG_PIXEL *Color)
445 {
446 UINTN x, y;
447 EG_PIXEL FillColor;
448 EG_PIXEL *PixelPtr;
449 EG_PIXEL *PixelBasePtr;
450
451 egRestrictImageArea(CompImage, AreaPosX, AreaPosY, &AreaWidth, &AreaHeight);
452
453 if (AreaWidth > 0) {
454 FillColor = *Color;
455 if (!CompImage->HasAlpha)
456 FillColor.a = 0;
457
458 PixelBasePtr = CompImage->PixelData + AreaPosY * CompImage->Width + AreaPosX;
459 for (y = 0; y < AreaHeight; y++) {
460 PixelPtr = PixelBasePtr;
461 for (x = 0; x < AreaWidth; x++, PixelPtr++)
462 *PixelPtr = FillColor;
463 PixelBasePtr += CompImage->Width;
464 }
465 }
466 }
467
468 VOID egRawCopy(IN OUT EG_PIXEL *CompBasePtr, IN EG_PIXEL *TopBasePtr,
469 IN UINTN Width, IN UINTN Height,
470 IN UINTN CompLineOffset, IN UINTN TopLineOffset)
471 {
472 UINTN x, y;
473 EG_PIXEL *TopPtr, *CompPtr;
474
475 for (y = 0; y < Height; y++) {
476 TopPtr = TopBasePtr;
477 CompPtr = CompBasePtr;
478 for (x = 0; x < Width; x++) {
479 *CompPtr = *TopPtr;
480 TopPtr++, CompPtr++;
481 }
482 TopBasePtr += TopLineOffset;
483 CompBasePtr += CompLineOffset;
484 }
485 }
486
487 VOID egRawCompose(IN OUT EG_PIXEL *CompBasePtr, IN EG_PIXEL *TopBasePtr,
488 IN UINTN Width, IN UINTN Height,
489 IN UINTN CompLineOffset, IN UINTN TopLineOffset)
490 {
491 UINTN x, y;
492 EG_PIXEL *TopPtr, *CompPtr;
493 UINTN Alpha;
494 UINTN RevAlpha;
495 UINTN Temp;
496
497 for (y = 0; y < Height; y++) {
498 TopPtr = TopBasePtr;
499 CompPtr = CompBasePtr;
500 for (x = 0; x < Width; x++) {
501 Alpha = TopPtr->a;
502 RevAlpha = 255 - Alpha;
503 Temp = (UINTN)CompPtr->b * RevAlpha + (UINTN)TopPtr->b * Alpha + 0x80;
504 CompPtr->b = (Temp + (Temp >> 8)) >> 8;
505 Temp = (UINTN)CompPtr->g * RevAlpha + (UINTN)TopPtr->g * Alpha + 0x80;
506 CompPtr->g = (Temp + (Temp >> 8)) >> 8;
507 Temp = (UINTN)CompPtr->r * RevAlpha + (UINTN)TopPtr->r * Alpha + 0x80;
508 CompPtr->r = (Temp + (Temp >> 8)) >> 8;
509 /*
510 CompPtr->b = ((UINTN)CompPtr->b * RevAlpha + (UINTN)TopPtr->b * Alpha) / 255;
511 CompPtr->g = ((UINTN)CompPtr->g * RevAlpha + (UINTN)TopPtr->g * Alpha) / 255;
512 CompPtr->r = ((UINTN)CompPtr->r * RevAlpha + (UINTN)TopPtr->r * Alpha) / 255;
513 */
514 TopPtr++, CompPtr++;
515 }
516 TopBasePtr += TopLineOffset;
517 CompBasePtr += CompLineOffset;
518 }
519 }
520
521 VOID egComposeImage(IN OUT EG_IMAGE *CompImage, IN EG_IMAGE *TopImage, IN UINTN PosX, IN UINTN PosY)
522 {
523 UINTN CompWidth, CompHeight;
524
525 CompWidth = TopImage->Width;
526 CompHeight = TopImage->Height;
527 egRestrictImageArea(CompImage, PosX, PosY, &CompWidth, &CompHeight);
528
529 // compose
530 if (CompWidth > 0) {
531 // if (CompImage->HasAlpha) {
532 // CompImage->HasAlpha = FALSE;
533 // egSetPlane(PLPTR(CompImage, a), 0, CompImage->Width * CompImage->Height);
534 // }
535
536 if (TopImage->HasAlpha) {
537 egRawCompose(CompImage->PixelData + PosY * CompImage->Width + PosX, TopImage->PixelData,
538 CompWidth, CompHeight, CompImage->Width, TopImage->Width);
539 } else {
540 egRawCopy(CompImage->PixelData + PosY * CompImage->Width + PosX, TopImage->PixelData,
541 CompWidth, CompHeight, CompImage->Width, TopImage->Width);
542 }
543 }
544 }
545
546 EG_IMAGE * egEnsureImageSize(IN EG_IMAGE *Image, IN UINTN Width, IN UINTN Height, IN EG_PIXEL *Color)
547 {
548 EG_IMAGE *NewImage;
549
550 if (Image == NULL)
551 return NULL;
552 if (Image->Width == Width && Image->Height == Height)
553 return Image;
554
555 NewImage = egCreateFilledImage(Width, Height, Image->HasAlpha, Color);
556 if (NewImage == NULL) {
557 egFreeImage(Image);
558 return NULL;
559 }
560 egComposeImage(NewImage, Image, 0, 0);
561 egFreeImage(Image);
562
563 return NewImage;
564 }
565
566 //
567 // misc internal functions
568 //
569
570 VOID egInsertPlane(IN UINT8 *SrcDataPtr, IN UINT8 *DestPlanePtr, IN UINTN PixelCount)
571 {
572 UINTN i;
573
574 for (i = 0; i < PixelCount; i++) {
575 *DestPlanePtr = *SrcDataPtr++;
576 DestPlanePtr += 4;
577 }
578 }
579
580 VOID egSetPlane(IN UINT8 *DestPlanePtr, IN UINT8 Value, IN UINTN PixelCount)
581 {
582 UINTN i;
583
584 for (i = 0; i < PixelCount; i++) {
585 *DestPlanePtr = Value;
586 DestPlanePtr += 4;
587 }
588 }
589
590 VOID egCopyPlane(IN UINT8 *SrcPlanePtr, IN UINT8 *DestPlanePtr, IN UINTN PixelCount)
591 {
592 UINTN i;
593
594 for (i = 0; i < PixelCount; i++) {
595 *DestPlanePtr = *SrcPlanePtr;
596 DestPlanePtr += 4, SrcPlanePtr += 4;
597 }
598 }
599
600 /* EOF */