]> code.delx.au - refind/blobdiff - libeg/image.c
Fixed crash related to float-to-int conversion problem on some systems (32-bit Mac...
[refind] / libeg / image.c
index 9f2262838cead9c9e3482e48395e04a29c2a8390..07f9086271d790244ef408d2e58ab60e55fc4468 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*
- * Modifications copyright (c) 2012-2013 Roderick W. Smith
- * 
+ * Modifications copyright (c) 2012-2015 Roderick W. Smith
+ *
  * Modifications distributed under the terms of the GNU General Public
- * License (GPL) version 3 (GPLv3), a copy of which must be distributed
- * with this source code or binaries made from it.
- * 
+ * License (GPL) version 3 (GPLv3), or (at your option) any later version.
+ *
+ */
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "libegint.h"
 
 #define MAX_FILE_SIZE (1024*1024*1024)
 
+// Multiplier for pseudo-floating-point operations in egScaleImage().
+// A value of 4096 should keep us within limits 32-bit systems, but I've seen
+// some minor artifacts at this level, so give it a bit more precision on
+// 64-bit systems....
+#if defined(EFIX64)
+#define FP_MULTIPLIER 32768
+#else
+#define FP_MULTIPLIER 4096
+#endif
+
 #ifndef __MAKEWITH_GNUEFI
 #define LibLocateHandle gBS->LocateHandleBuffer
 #define LibOpenRoot EfiLibOpenRoot
@@ -69,7 +92,7 @@ EG_IMAGE * egCreateImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha)
         return NULL;
     NewImage->PixelData = (EG_PIXEL *) AllocatePool(Width * Height * sizeof(EG_PIXEL));
     if (NewImage->PixelData == NULL) {
-        FreePool(NewImage);
+        egFreeImage(NewImage);
         return NULL;
     }
 
@@ -125,6 +148,77 @@ EG_IMAGE * egCropImage(IN EG_IMAGE *Image, IN UINTN StartX, IN UINTN StartY, IN
    return NewImage;
 } // EG_IMAGE * egCropImage()
 
+// The following function implements a bilinear image scaling algorithm, based on
+// code presented at http://tech-algorithm.com/articles/bilinear-image-scaling/.
+// Resize an image; returns pointer to resized image if successful, NULL otherwise.
+// Calling function is responsible for freeing allocated memory.
+// NOTE: x_ratio, y_ratio, x_diff, and y_diff should really be float values;
+// however, I've found that my 32-bit Mac Mini has a buggy EFI (or buggy CPU?), which
+// causes this function to hang on float-to-UINT8 conversions on some (but not all!)
+// float values. Therefore, this function uses integer arithmetic but multiplies
+// all values by FP_MULTIPLIER to achieve something resembling the sort of precision
+// needed for good results.
+EG_IMAGE * egScaleImage(IN EG_IMAGE *Image, IN UINTN NewWidth, IN UINTN NewHeight) {
+   EG_IMAGE *NewImage = NULL;
+   EG_PIXEL a, b, c, d;
+   UINTN x, y, Index ;
+   UINTN i, j;
+   UINTN Offset = 0;
+   UINTN x_ratio, y_ratio, x_diff, y_diff;
+
+   if ((Image == NULL) || (Image->Height == 0) || (Image->Width == 0) || (NewWidth == 0) || (NewHeight == 0))
+      return NULL;
+
+   if ((Image->Width == NewWidth) && (Image->Height == NewHeight))
+      return (egCopyImage(Image));
+
+   NewImage = egCreateImage(NewWidth, NewHeight, Image->HasAlpha);
+   if (NewImage == NULL)
+      return NULL;
+
+   x_ratio = ((Image->Width - 1) * FP_MULTIPLIER) / NewWidth;
+   y_ratio = ((Image->Height - 1) * FP_MULTIPLIER) / NewHeight;
+
+   for (i = 0; i < NewHeight; i++) {
+      for (j = 0; j < NewWidth; j++) {
+         x = (j * (Image->Width - 1)) / NewWidth;
+         y = (i * (Image->Height - 1)) / NewHeight;
+         x_diff = (x_ratio * j) - x * FP_MULTIPLIER;
+         y_diff = (y_ratio * i) - y * FP_MULTIPLIER;
+         Index = ((y * Image->Width) + x);
+         a = Image->PixelData[Index];
+         b = Image->PixelData[Index + 1];
+         c = Image->PixelData[Index + Image->Width];
+         d = Image->PixelData[Index + Image->Width + 1];
+
+         // blue element
+         NewImage->PixelData[Offset].b = ((a.b) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) +
+                                          (b.b) * (x_diff) * (FP_MULTIPLIER - y_diff) +
+                                          (c.b) * (y_diff) * (FP_MULTIPLIER - x_diff) +
+                                          (d.b) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER);
+
+         // green element
+         NewImage->PixelData[Offset].g = ((a.g) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) +
+                                          (b.g) * (x_diff) * (FP_MULTIPLIER - y_diff) +
+                                          (c.g) * (y_diff) * (FP_MULTIPLIER - x_diff) +
+                                          (d.g) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER);
+
+         // red element
+         NewImage->PixelData[Offset].r = ((a.r) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) +
+                                          (b.r) * (x_diff) * (FP_MULTIPLIER - y_diff) +
+                                          (c.r) * (y_diff) * (FP_MULTIPLIER - x_diff) +
+                                          (d.r) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER);
+
+         // alpha element
+         NewImage->PixelData[Offset++].a = ((a.a) * (FP_MULTIPLIER - x_diff) * (FP_MULTIPLIER - y_diff) +
+                                            (b.a) * (x_diff) * (FP_MULTIPLIER - y_diff) +
+                                            (c.a) * (y_diff) * (FP_MULTIPLIER - x_diff) +
+                                            (d.a) * (x_diff * y_diff)) / (FP_MULTIPLIER * FP_MULTIPLIER);
+      } // for (j...)
+   } // for (i...)
+   return NewImage;
+} // EG_IMAGE * egScaleImage()
+
 VOID egFreeImage(IN EG_IMAGE *Image)
 {
     if (Image != NULL) {
@@ -138,7 +232,7 @@ VOID egFreeImage(IN EG_IMAGE *Image)
 // Basic file operations
 //
 
-EFI_STATUS egLoadFile(IN EFI_FILEBaseDir, IN CHAR16 *FileName, OUT UINT8 **FileData, OUT UINTN *FileDataLength)
+EFI_STATUS egLoadFile(IN EFI_FILE *BaseDir, IN CHAR16 *FileName, OUT UINT8 **FileData, OUT UINTN *FileDataLength)
 {
     EFI_STATUS          Status;
     EFI_FILE_HANDLE     FileHandle;
@@ -147,6 +241,9 @@ EFI_STATUS egLoadFile(IN EFI_FILE* BaseDir, IN CHAR16 *FileName, OUT UINT8 **Fil
     UINTN               BufferSize;
     UINT8               *Buffer;
 
+    if ((BaseDir == NULL) || (FileName == NULL))
+       return EFI_NOT_FOUND;
+
     Status = refit_call5_wrapper(BaseDir->Open, BaseDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
     if (EFI_ERROR(Status)) {
         return Status;
@@ -260,7 +357,7 @@ EG_IMAGE * egLoadImage(IN EFI_FILE* BaseDir, IN CHAR16 *FileName, IN BOOLEAN Wan
         return NULL;
 
     // decode it
-    NewImage = egDecodeAny(FileData, FileDataLength, 128, WantAlpha);
+    NewImage = egDecodeAny(FileData, FileDataLength, 128 /* arbitrary value */, WantAlpha);
     FreePool(FileData);
 
     return NewImage;
@@ -273,7 +370,7 @@ EG_IMAGE * egLoadIcon(IN EFI_FILE* BaseDir, IN CHAR16 *Path, IN UINTN IconSize)
     EFI_STATUS      Status;
     UINT8           *FileData;
     UINTN           FileDataLength;
-    EG_IMAGE        *NewImage;
+    EG_IMAGE        *Image, *NewImage;
 
     if (BaseDir == NULL || Path == NULL)
         return NULL;
@@ -284,15 +381,17 @@ EG_IMAGE * egLoadIcon(IN EFI_FILE* BaseDir, IN CHAR16 *Path, IN UINTN IconSize)
        return NULL;
 
     // decode it
-    NewImage = egDecodeAny(FileData, FileDataLength, IconSize, TRUE);
+    Image = egDecodeAny(FileData, FileDataLength, IconSize, TRUE);
     FreePool(FileData);
-    if ((NewImage->Width != IconSize) || (NewImage->Height != IconSize)) {
-       Print(L"Warning: Attempt to load icon of the wrong size from '%s'\n", Path);
-       MyFreePool(NewImage);
-       NewImage = NULL;
+    if ((Image->Width != IconSize) || (Image->Height != IconSize)) {
+       NewImage = egScaleImage(Image, IconSize, IconSize);
+       if (!NewImage)
+          Print(L"Warning: Unable to scale icon of the wrong size from '%s'\n", Path);
+       egFreeImage(Image);
+       Image = NewImage;
     }
 
-    return NewImage;
+    return Image;
 } // EG_IMAGE *egLoadIcon()
 
 // Returns an icon of any type from the specified subdirectory using the specified
@@ -554,27 +653,6 @@ VOID egComposeImage(IN OUT EG_IMAGE *CompImage, IN EG_IMAGE *TopImage, IN UINTN
     }
 } /* VOID egComposeImage() */
 
-EG_IMAGE * egEnsureImageSize(IN EG_IMAGE *Image, IN UINTN Width, IN UINTN Height, IN EG_PIXEL *Color)
-{
-    EG_IMAGE *NewImage;
-
-    if (Image == NULL)
-        return NULL;
-    if (Image->Width == Width && Image->Height == Height)
-        return Image;
-
-    NewImage = egCreateFilledImage(Width, Height, Image->HasAlpha, Color);
-    if (NewImage == NULL) {
-        egFreeImage(Image);
-        return NULL;
-    }
-    Image->HasAlpha = FALSE;
-    egComposeImage(NewImage, Image, 0, 0);
-    egFreeImage(Image);
-
-    return NewImage;
-}
-
 //
 // misc internal functions
 //