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