]> code.delx.au - refind/blob - libeg/image.c
Add code to spoof OSX booting for other OSes (on Macs)
[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 * Modifications copyright (c) 2012-2015 Roderick W. Smith
38 *
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), or (at your option) any later version.
41 *
42 */
43 /*
44 * This program is free software: you can redistribute it and/or modify
45 * it under the terms of the GNU General Public License as published by
46 * the Free Software Foundation, either version 3 of the License, or
47 * (at your option) any later version.
48 *
49 * This program is distributed in the hope that it will be useful,
50 * but WITHOUT ANY WARRANTY; without even the implied warranty of
51 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52 * GNU General Public License for more details.
53 *
54 * You should have received a copy of the GNU General Public License
55 * along with this program. If not, see <http://www.gnu.org/licenses/>.
56 */
57
58 #include "libegint.h"
59 #include "../refind/global.h"
60 #include "../refind/lib.h"
61 #include "../refind/screen.h"
62 #include "../include/refit_call_wrapper.h"
63 #include "lodepng.h"
64
65 #define MAX_FILE_SIZE (1024*1024*1024)
66
67 // Multiplier for pseudo-floating-point operations in egScaleImage().
68 // A value of 4096 should keep us within limits on 32-bit systems, but I've
69 // seen some minor artifacts at this level, so give it a bit more precision
70 // on 64-bit systems....
71 #if defined(EFIX64)
72 #define FP_MULTIPLIER (UINTN) 65536
73 #else
74 #define FP_MULTIPLIER (UINTN) 4096
75 #endif
76
77 #ifndef __MAKEWITH_GNUEFI
78 #define LibLocateHandle gBS->LocateHandleBuffer
79 #define LibOpenRoot EfiLibOpenRoot
80 #endif
81
82 //
83 // Basic image handling
84 //
85
86 EG_IMAGE * egCreateImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha)
87 {
88 EG_IMAGE *NewImage;
89
90 NewImage = (EG_IMAGE *) AllocatePool(sizeof(EG_IMAGE));
91 if (NewImage == NULL)
92 return NULL;
93 NewImage->PixelData = (EG_PIXEL *) AllocatePool(Width * Height * sizeof(EG_PIXEL));
94 if (NewImage->PixelData == NULL) {
95 egFreeImage(NewImage);
96 return NULL;
97 }
98
99 NewImage->Width = Width;
100 NewImage->Height = Height;
101 NewImage->HasAlpha = HasAlpha;
102 return NewImage;
103 }
104
105 EG_IMAGE * egCreateFilledImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha, IN EG_PIXEL *Color)
106 {
107 EG_IMAGE *NewImage;
108
109 NewImage = egCreateImage(Width, Height, HasAlpha);
110 if (NewImage == NULL)
111 return NULL;
112
113 egFillImage(NewImage, Color);
114 return NewImage;
115 }
116
117 EG_IMAGE * egCopyImage(IN EG_IMAGE *Image)
118 {
119 EG_IMAGE *NewImage = NULL;
120
121 if (Image != NULL)
122 NewImage = egCreateImage(Image->Width, Image->Height, Image->HasAlpha);
123 if (NewImage == NULL)
124 return NULL;
125
126 CopyMem(NewImage->PixelData, Image->PixelData, Image->Width * Image->Height * sizeof(EG_PIXEL));
127 return NewImage;
128 }
129
130 // Returns a smaller image composed of the specified crop area from the larger area.
131 // If the specified area is larger than is in the original, returns NULL.
132 EG_IMAGE * egCropImage(IN EG_IMAGE *Image, IN UINTN StartX, IN UINTN StartY, IN UINTN Width, IN UINTN Height) {
133 EG_IMAGE *NewImage = NULL;
134 UINTN x, y;
135
136 if (((StartX + Width) > Image->Width) || ((StartY + Height) > Image->Height))
137 return NULL;
138
139 NewImage = egCreateImage(Width, Height, Image->HasAlpha);
140 if (NewImage == NULL)
141 return NULL;
142
143 for (y = 0; y < Height; y++) {
144 for (x = 0; x < Width; x++) {
145 NewImage->PixelData[y * NewImage->Width + x] = Image->PixelData[(y + StartY) * Image->Width + x + StartX];
146 }
147 }
148 return NewImage;
149 } // EG_IMAGE * egCropImage()
150
151 // The following function implements a bilinear image scaling algorithm, based on
152 // code presented at http://tech-algorithm.com/articles/bilinear-image-scaling/.
153 // Resize an image; returns pointer to resized image if successful, NULL otherwise.
154 // Calling function is responsible for freeing allocated memory.
155 // NOTE: x_ratio, y_ratio, x_diff, and y_diff should really be float values;
156 // however, I've found that my 32-bit Mac Mini has a buggy EFI (or buggy CPU?), which
157 // causes this function to hang on float-to-UINT8 conversions on some (but not all!)
158 // float values. Therefore, this function uses integer arithmetic but multiplies
159 // all values by FP_MULTIPLIER to achieve something resembling the sort of precision
160 // needed for good results.
161 EG_IMAGE * egScaleImage(IN EG_IMAGE *Image, IN UINTN NewWidth, IN UINTN NewHeight) {
162 EG_IMAGE *NewImage = NULL;
163 EG_PIXEL a, b, c, d;
164 UINTN x, y, Index ;
165 UINTN i, j;
166 UINTN Offset = 0;
167 UINTN x_ratio, y_ratio, x_diff, y_diff;
168
169 if ((Image == NULL) || (Image->Height == 0) || (Image->Width == 0) || (NewWidth == 0) || (NewHeight == 0))
170 return NULL;
171
172 if ((Image->Width == NewWidth) && (Image->Height == NewHeight))
173 return (egCopyImage(Image));
174
175 NewImage = egCreateImage(NewWidth, NewHeight, Image->HasAlpha);
176 if (NewImage == NULL)
177 return NULL;
178
179 x_ratio = ((Image->Width - 1) * FP_MULTIPLIER) / NewWidth;
180 y_ratio = ((Image->Height - 1) * FP_MULTIPLIER) / NewHeight;
181
182 for (i = 0; i < NewHeight; i++) {
183 for (j = 0; j < NewWidth; j++) {
184 x = (j * (Image->Width - 1)) / NewWidth;
185 y = (i * (Image->Height - 1)) / NewHeight;
186 x_diff = (x_ratio * j) - x * FP_MULTIPLIER;
187 y_diff = (y_ratio * i) - y * FP_MULTIPLIER;
188 Index = ((y * Image->Width) + x);
189 a = Image->PixelData[Index];
190 b = Image->PixelData[Index + 1];
191 c = Image->PixelData[Index + Image->Width];
192 d = Image->PixelData[Index + Image->Width + 1];
193
194 // blue element
195 NewImage->PixelData[Offset].b = ((a.b) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) +
196 (b.b) * (x_diff) * (FP_MULTIPLIER - y_diff) +
197 (c.b) * (y_diff) * (FP_MULTIPLIER - x_diff) +
198 (d.b) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER);
199
200 // green element
201 NewImage->PixelData[Offset].g = ((a.g) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) +
202 (b.g) * (x_diff) * (FP_MULTIPLIER - y_diff) +
203 (c.g) * (y_diff) * (FP_MULTIPLIER - x_diff) +
204 (d.g) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER);
205
206 // red element
207 NewImage->PixelData[Offset].r = ((a.r) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) +
208 (b.r) * (x_diff) * (FP_MULTIPLIER - y_diff) +
209 (c.r) * (y_diff) * (FP_MULTIPLIER - x_diff) +
210 (d.r) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER);
211
212 // alpha element
213 NewImage->PixelData[Offset++].a = ((a.a) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) +
214 (b.a) * (x_diff) * (FP_MULTIPLIER - y_diff) +
215 (c.a) * (y_diff) * (FP_MULTIPLIER - x_diff) +
216 (d.a) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER);
217 } // for (j...)
218 } // for (i...)
219 return NewImage;
220 } // EG_IMAGE * egScaleImage()
221
222 VOID egFreeImage(IN EG_IMAGE *Image)
223 {
224 if (Image != NULL) {
225 if (Image->PixelData != NULL)
226 FreePool(Image->PixelData);
227 FreePool(Image);
228 }
229 }
230
231 //
232 // Basic file operations
233 //
234
235 EFI_STATUS egLoadFile(IN EFI_FILE *BaseDir, IN CHAR16 *FileName, OUT UINT8 **FileData, OUT UINTN *FileDataLength)
236 {
237 EFI_STATUS Status;
238 EFI_FILE_HANDLE FileHandle;
239 EFI_FILE_INFO *FileInfo;
240 UINT64 ReadSize;
241 UINTN BufferSize;
242 UINT8 *Buffer;
243
244 if ((BaseDir == NULL) || (FileName == NULL))
245 return EFI_NOT_FOUND;
246
247 Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
248 if (EFI_ERROR(Status)) {
249 return Status;
250 }
251
252 FileInfo = LibFileInfo(FileHandle);
253 if (FileInfo == NULL) {
254 refit_call1_wrapper(FileHandle->Close, FileHandle);
255 return EFI_NOT_FOUND;
256 }
257 ReadSize = FileInfo->FileSize;
258 if (ReadSize > MAX_FILE_SIZE)
259 ReadSize = MAX_FILE_SIZE;
260 FreePool(FileInfo);
261
262 BufferSize = (UINTN)ReadSize; // was limited to 1 GB above, so this is safe
263 Buffer = (UINT8 *) AllocatePool(BufferSize);
264 if (Buffer == NULL) {
265 refit_call1_wrapper(FileHandle->Close, FileHandle);
266 return EFI_OUT_OF_RESOURCES;
267 }
268
269 Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &BufferSize, Buffer);
270 refit_call1_wrapper(FileHandle->Close, FileHandle);
271 if (EFI_ERROR(Status)) {
272 FreePool(Buffer);
273 return Status;
274 }
275
276 *FileData = Buffer;
277 *FileDataLength = BufferSize;
278 return EFI_SUCCESS;
279 }
280
281 static EFI_GUID ESPGuid = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
282
283 EFI_STATUS egFindESP(OUT EFI_FILE_HANDLE *RootDir)
284 {
285 EFI_STATUS Status;
286 UINTN HandleCount = 0;
287 EFI_HANDLE *Handles;
288
289 Status = LibLocateHandle(ByProtocol, &ESPGuid, NULL, &HandleCount, &Handles);
290 if (!EFI_ERROR(Status) && HandleCount > 0) {
291 *RootDir = LibOpenRoot(Handles[0]);
292 if (*RootDir == NULL)
293 Status = EFI_NOT_FOUND;
294 FreePool(Handles);
295 }
296 return Status;
297 }
298
299 EFI_STATUS egSaveFile(IN EFI_FILE* BaseDir OPTIONAL, IN CHAR16 *FileName,
300 IN UINT8 *FileData, IN UINTN FileDataLength)
301 {
302 EFI_STATUS Status;
303 EFI_FILE_HANDLE FileHandle;
304 UINTN BufferSize;
305
306 if (BaseDir == NULL) {
307 Status = egFindESP(&BaseDir);
308 if (EFI_ERROR(Status))
309 return Status;
310 }
311
312 Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName,
313 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
314 if (EFI_ERROR(Status))
315 return Status;
316
317 BufferSize = FileDataLength;
318 Status = refit_call3_wrapper(FileHandle->Write, FileHandle, &BufferSize, FileData);
319 refit_call1_wrapper(FileHandle->Close, FileHandle);
320
321 return Status;
322 }
323
324 //
325 // Loading images from files and embedded data
326 //
327
328 // Decode the specified image data. The IconSize parameter is relevant only
329 // for ICNS, for which it selects which ICNS sub-image is decoded.
330 // Returns a pointer to the resulting EG_IMAGE or NULL if decoding failed.
331 static EG_IMAGE * egDecodeAny(IN UINT8 *FileData, IN UINTN FileDataLength, IN UINTN IconSize, IN BOOLEAN WantAlpha)
332 {
333 EG_IMAGE *NewImage = NULL;
334
335 NewImage = egDecodeICNS(FileData, FileDataLength, IconSize, WantAlpha);
336 if (NewImage == NULL)
337 NewImage = egDecodePNG(FileData, FileDataLength, IconSize, WantAlpha);
338 if (NewImage == NULL)
339 NewImage = egDecodeBMP(FileData, FileDataLength, IconSize, WantAlpha);
340
341 return NewImage;
342 }
343
344 EG_IMAGE * egLoadImage(IN EFI_FILE* BaseDir, IN CHAR16 *FileName, IN BOOLEAN WantAlpha)
345 {
346 EFI_STATUS Status;
347 UINT8 *FileData;
348 UINTN FileDataLength;
349 EG_IMAGE *NewImage;
350
351 if (BaseDir == NULL || FileName == NULL)
352 return NULL;
353
354 // load file
355 Status = egLoadFile(BaseDir, FileName, &FileData, &FileDataLength);
356 if (EFI_ERROR(Status))
357 return NULL;
358
359 // decode it
360 NewImage = egDecodeAny(FileData, FileDataLength, 128 /* arbitrary value */, WantAlpha);
361 FreePool(FileData);
362
363 return NewImage;
364 }
365
366 // Load an icon from (BaseDir)/Path, extracting the icon of size IconSize x IconSize.
367 // Returns a pointer to the image data, or NULL if the icon could not be loaded.
368 EG_IMAGE * egLoadIcon(IN EFI_FILE* BaseDir, IN CHAR16 *Path, IN UINTN IconSize)
369 {
370 EFI_STATUS Status;
371 UINT8 *FileData;
372 UINTN FileDataLength;
373 EG_IMAGE *Image, *NewImage;
374
375 if (BaseDir == NULL || Path == NULL)
376 return NULL;
377
378 // load file
379 Status = egLoadFile(BaseDir, Path, &FileData, &FileDataLength);
380 if (EFI_ERROR(Status))
381 return NULL;
382
383 // decode it
384 Image = egDecodeAny(FileData, FileDataLength, IconSize, TRUE);
385 FreePool(FileData);
386 if ((Image->Width != IconSize) || (Image->Height != IconSize)) {
387 NewImage = egScaleImage(Image, IconSize, IconSize);
388 if (!NewImage)
389 Print(L"Warning: Unable to scale icon of the wrong size from '%s'\n", Path);
390 egFreeImage(Image);
391 Image = NewImage;
392 }
393
394 return Image;
395 } // EG_IMAGE *egLoadIcon()
396
397 // Returns an icon of any type from the specified subdirectory using the specified
398 // base name. All directory references are relative to BaseDir. For instance, if
399 // SubdirName is "myicons" and BaseName is "os_linux", this function will return
400 // an image based on "myicons/os_linux.icns" or "myicons/os_linux.png", in that
401 // order of preference. Returns NULL if no such file is a valid icon file.
402 EG_IMAGE * egLoadIconAnyType(IN EFI_FILE *BaseDir, IN CHAR16 *SubdirName, IN CHAR16 *BaseName, IN UINTN IconSize) {
403 EG_IMAGE *Image = NULL;
404 CHAR16 *Extension;
405 CHAR16 FileName[256];
406 UINTN i = 0;
407
408 while (((Extension = FindCommaDelimited(ICON_EXTENSIONS, i++)) != NULL) && (Image == NULL)) {
409 SPrint(FileName, 255, L"%s\\%s.%s", SubdirName, BaseName, Extension);
410 Image = egLoadIcon(BaseDir, FileName, IconSize);
411 MyFreePool(Extension);
412 } // while()
413
414 return Image;
415 } // EG_IMAGE *egLoadIconAnyType()
416
417 // Returns an icon with any extension in ICON_EXTENSIONS from either the directory
418 // specified by GlobalConfig.IconsDir or DEFAULT_ICONS_DIR. The input BaseName
419 // should be the icon name without an extension. For instance, if BaseName is
420 // os_linux, GlobalConfig.IconsDir is myicons, DEFAULT_ICONS_DIR is icons, and
421 // ICON_EXTENSIONS is "icns,png", this function will return myicons/os_linux.icns,
422 // myicons/os_linux.png, icons/os_linux.icns, or icons/os_linux.png, in that
423 // order of preference. Returns NULL if no such icon can be found. All file
424 // references are relative to SelfDir.
425 EG_IMAGE * egFindIcon(IN CHAR16 *BaseName, IN UINTN IconSize) {
426 EG_IMAGE *Image = NULL;
427
428 if (GlobalConfig.IconsDir != NULL) {
429 Image = egLoadIconAnyType(SelfDir, GlobalConfig.IconsDir, BaseName, IconSize);
430 }
431
432 if (Image == NULL) {
433 Image = egLoadIconAnyType(SelfDir, DEFAULT_ICONS_DIR, BaseName, IconSize);
434 }
435
436 return Image;
437 } // EG_IMAGE * egFindIcon()
438
439 EG_IMAGE * egPrepareEmbeddedImage(IN EG_EMBEDDED_IMAGE *EmbeddedImage, IN BOOLEAN WantAlpha)
440 {
441 EG_IMAGE *NewImage;
442 UINT8 *CompData;
443 UINTN CompLen;
444 UINTN PixelCount;
445
446 // sanity check
447 if (EmbeddedImage->PixelMode > EG_MAX_EIPIXELMODE ||
448 (EmbeddedImage->CompressMode != EG_EICOMPMODE_NONE && EmbeddedImage->CompressMode != EG_EICOMPMODE_RLE))
449 return NULL;
450
451 // allocate image structure and pixel buffer
452 NewImage = egCreateImage(EmbeddedImage->Width, EmbeddedImage->Height, WantAlpha);
453 if (NewImage == NULL)
454 return NULL;
455
456 CompData = (UINT8 *)EmbeddedImage->Data; // drop const
457 CompLen = EmbeddedImage->DataLength;
458 PixelCount = EmbeddedImage->Width * EmbeddedImage->Height;
459
460 // FUTURE: for EG_EICOMPMODE_EFICOMPRESS, decompress whole data block here
461
462 if (EmbeddedImage->PixelMode == EG_EIPIXELMODE_GRAY ||
463 EmbeddedImage->PixelMode == EG_EIPIXELMODE_GRAY_ALPHA) {
464
465 // copy grayscale plane and expand
466 if (EmbeddedImage->CompressMode == EG_EICOMPMODE_RLE) {
467 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, r), PixelCount);
468 } else {
469 egInsertPlane(CompData, PLPTR(NewImage, r), PixelCount);
470 CompData += PixelCount;
471 }
472 egCopyPlane(PLPTR(NewImage, r), PLPTR(NewImage, g), PixelCount);
473 egCopyPlane(PLPTR(NewImage, r), PLPTR(NewImage, b), PixelCount);
474
475 } else if (EmbeddedImage->PixelMode == EG_EIPIXELMODE_COLOR ||
476 EmbeddedImage->PixelMode == EG_EIPIXELMODE_COLOR_ALPHA) {
477
478 // copy color planes
479 if (EmbeddedImage->CompressMode == EG_EICOMPMODE_RLE) {
480 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, r), PixelCount);
481 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, g), PixelCount);
482 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, b), PixelCount);
483 } else {
484 egInsertPlane(CompData, PLPTR(NewImage, r), PixelCount);
485 CompData += PixelCount;
486 egInsertPlane(CompData, PLPTR(NewImage, g), PixelCount);
487 CompData += PixelCount;
488 egInsertPlane(CompData, PLPTR(NewImage, b), PixelCount);
489 CompData += PixelCount;
490 }
491
492 } else {
493
494 // set color planes to black
495 egSetPlane(PLPTR(NewImage, r), 0, PixelCount);
496 egSetPlane(PLPTR(NewImage, g), 0, PixelCount);
497 egSetPlane(PLPTR(NewImage, b), 0, PixelCount);
498
499 }
500
501 if (WantAlpha && (EmbeddedImage->PixelMode == EG_EIPIXELMODE_GRAY_ALPHA ||
502 EmbeddedImage->PixelMode == EG_EIPIXELMODE_COLOR_ALPHA ||
503 EmbeddedImage->PixelMode == EG_EIPIXELMODE_ALPHA)) {
504
505 // copy alpha plane
506 if (EmbeddedImage->CompressMode == EG_EICOMPMODE_RLE) {
507 egDecompressIcnsRLE(&CompData, &CompLen, PLPTR(NewImage, a), PixelCount);
508 } else {
509 egInsertPlane(CompData, PLPTR(NewImage, a), PixelCount);
510 CompData += PixelCount;
511 }
512
513 } else {
514 egSetPlane(PLPTR(NewImage, a), WantAlpha ? 255 : 0, PixelCount);
515 }
516
517 return NewImage;
518 }
519
520 //
521 // Compositing
522 //
523
524 VOID egRestrictImageArea(IN EG_IMAGE *Image,
525 IN UINTN AreaPosX, IN UINTN AreaPosY,
526 IN OUT UINTN *AreaWidth, IN OUT UINTN *AreaHeight)
527 {
528 if (AreaPosX >= Image->Width || AreaPosY >= Image->Height) {
529 // out of bounds, operation has no effect
530 *AreaWidth = 0;
531 *AreaHeight = 0;
532 } else {
533 // calculate affected area
534 if (*AreaWidth > Image->Width - AreaPosX)
535 *AreaWidth = Image->Width - AreaPosX;
536 if (*AreaHeight > Image->Height - AreaPosY)
537 *AreaHeight = Image->Height - AreaPosY;
538 }
539 }
540
541 VOID egFillImage(IN OUT EG_IMAGE *CompImage, IN EG_PIXEL *Color)
542 {
543 UINTN i;
544 EG_PIXEL FillColor;
545 EG_PIXEL *PixelPtr;
546
547 FillColor = *Color;
548 if (!CompImage->HasAlpha)
549 FillColor.a = 0;
550
551 PixelPtr = CompImage->PixelData;
552 for (i = 0; i < CompImage->Width * CompImage->Height; i++, PixelPtr++)
553 *PixelPtr = FillColor;
554 }
555
556 VOID egFillImageArea(IN OUT EG_IMAGE *CompImage,
557 IN UINTN AreaPosX, IN UINTN AreaPosY,
558 IN UINTN AreaWidth, IN UINTN AreaHeight,
559 IN EG_PIXEL *Color)
560 {
561 UINTN x, y;
562 EG_PIXEL FillColor;
563 EG_PIXEL *PixelPtr;
564 EG_PIXEL *PixelBasePtr;
565
566 egRestrictImageArea(CompImage, AreaPosX, AreaPosY, &AreaWidth, &AreaHeight);
567
568 if (AreaWidth > 0) {
569 FillColor = *Color;
570 if (!CompImage->HasAlpha)
571 FillColor.a = 0;
572
573 PixelBasePtr = CompImage->PixelData + AreaPosY * CompImage->Width + AreaPosX;
574 for (y = 0; y < AreaHeight; y++) {
575 PixelPtr = PixelBasePtr;
576 for (x = 0; x < AreaWidth; x++, PixelPtr++)
577 *PixelPtr = FillColor;
578 PixelBasePtr += CompImage->Width;
579 }
580 }
581 }
582
583 VOID egRawCopy(IN OUT EG_PIXEL *CompBasePtr, IN EG_PIXEL *TopBasePtr,
584 IN UINTN Width, IN UINTN Height,
585 IN UINTN CompLineOffset, IN UINTN TopLineOffset)
586 {
587 UINTN x, y;
588 EG_PIXEL *TopPtr, *CompPtr;
589
590 for (y = 0; y < Height; y++) {
591 TopPtr = TopBasePtr;
592 CompPtr = CompBasePtr;
593 for (x = 0; x < Width; x++) {
594 *CompPtr = *TopPtr;
595 TopPtr++, CompPtr++;
596 }
597 TopBasePtr += TopLineOffset;
598 CompBasePtr += CompLineOffset;
599 }
600 }
601
602 VOID egRawCompose(IN OUT EG_PIXEL *CompBasePtr, IN EG_PIXEL *TopBasePtr,
603 IN UINTN Width, IN UINTN Height,
604 IN UINTN CompLineOffset, IN UINTN TopLineOffset)
605 {
606 UINTN x, y;
607 EG_PIXEL *TopPtr, *CompPtr;
608 UINTN Alpha;
609 UINTN RevAlpha;
610 UINTN Temp;
611
612 for (y = 0; y < Height; y++) {
613 TopPtr = TopBasePtr;
614 CompPtr = CompBasePtr;
615 for (x = 0; x < Width; x++) {
616 Alpha = TopPtr->a;
617 RevAlpha = 255 - Alpha;
618 Temp = (UINTN)CompPtr->b * RevAlpha + (UINTN)TopPtr->b * Alpha + 0x80;
619 CompPtr->b = (Temp + (Temp >> 8)) >> 8;
620 Temp = (UINTN)CompPtr->g * RevAlpha + (UINTN)TopPtr->g * Alpha + 0x80;
621 CompPtr->g = (Temp + (Temp >> 8)) >> 8;
622 Temp = (UINTN)CompPtr->r * RevAlpha + (UINTN)TopPtr->r * Alpha + 0x80;
623 CompPtr->r = (Temp + (Temp >> 8)) >> 8;
624 /*
625 CompPtr->b = ((UINTN)CompPtr->b * RevAlpha + (UINTN)TopPtr->b * Alpha) / 255;
626 CompPtr->g = ((UINTN)CompPtr->g * RevAlpha + (UINTN)TopPtr->g * Alpha) / 255;
627 CompPtr->r = ((UINTN)CompPtr->r * RevAlpha + (UINTN)TopPtr->r * Alpha) / 255;
628 */
629 TopPtr++, CompPtr++;
630 }
631 TopBasePtr += TopLineOffset;
632 CompBasePtr += CompLineOffset;
633 }
634 }
635
636 VOID egComposeImage(IN OUT EG_IMAGE *CompImage, IN EG_IMAGE *TopImage, IN UINTN PosX, IN UINTN PosY)
637 {
638 UINTN CompWidth, CompHeight;
639
640 CompWidth = TopImage->Width;
641 CompHeight = TopImage->Height;
642 egRestrictImageArea(CompImage, PosX, PosY, &CompWidth, &CompHeight);
643
644 // compose
645 if (CompWidth > 0) {
646 if (TopImage->HasAlpha) {
647 egRawCompose(CompImage->PixelData + PosY * CompImage->Width + PosX, TopImage->PixelData,
648 CompWidth, CompHeight, CompImage->Width, TopImage->Width);
649 } else {
650 egRawCopy(CompImage->PixelData + PosY * CompImage->Width + PosX, TopImage->PixelData,
651 CompWidth, CompHeight, CompImage->Width, TopImage->Width);
652 }
653 }
654 } /* VOID egComposeImage() */
655
656 //
657 // misc internal functions
658 //
659
660 VOID egInsertPlane(IN UINT8 *SrcDataPtr, IN UINT8 *DestPlanePtr, IN UINTN PixelCount)
661 {
662 UINTN i;
663
664 for (i = 0; i < PixelCount; i++) {
665 *DestPlanePtr = *SrcDataPtr++;
666 DestPlanePtr += 4;
667 }
668 }
669
670 VOID egSetPlane(IN UINT8 *DestPlanePtr, IN UINT8 Value, IN UINTN PixelCount)
671 {
672 UINTN i;
673
674 for (i = 0; i < PixelCount; i++) {
675 *DestPlanePtr = Value;
676 DestPlanePtr += 4;
677 }
678 }
679
680 VOID egCopyPlane(IN UINT8 *SrcPlanePtr, IN UINT8 *DestPlanePtr, IN UINTN PixelCount)
681 {
682 UINTN i;
683
684 for (i = 0; i < PixelCount; i++) {
685 *DestPlanePtr = *SrcPlanePtr;
686 DestPlanePtr += 4, SrcPlanePtr += 4;
687 }
688 }
689
690 /* EOF */