]> code.delx.au - refind/blobdiff - libeg/image.c
Refinements to image-sizing code; 0.7.8 release version.
[refind] / libeg / image.c
index 9f2262838cead9c9e3482e48395e04a29c2a8390..0322d12a3ae04d53c04e407a3b0ee8f1fbc8b336 100644 (file)
@@ -34,7 +34,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 /*
- * Modifications copyright (c) 2012-2013 Roderick W. Smith
+ * Modifications copyright (c) 2012-2014 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
@@ -69,7 +69,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 +125,67 @@ 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.
+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;
+   float 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 = ((float)(Image->Width - 1)) / NewWidth;
+   y_ratio = ((float)(Image->Height - 1)) / NewHeight;
+
+   for (i = 0; i < NewHeight; i++) {
+      for (j = 0; j < NewWidth; j++) {
+         x = (UINTN)(x_ratio * j);
+         y = (UINTN)(y_ratio * i);
+         x_diff = (x_ratio * j) - x;
+         y_diff = (y_ratio * i) - y;
+         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
+         // Yb = Ab(1-Image->Width)(1-Image->Height) + Bb(Image->Width)(1-Image->Height) + Cb(Image->Height)(1-Image->Width) + Db(wh)
+         NewImage->PixelData[Offset].b = (a.b)*(1-x_diff)*(1-y_diff) + (b.b)*(x_diff)*(1-y_diff) +
+                                         (c.b)*(y_diff)*(1-x_diff)   + (d.b)*(x_diff*y_diff);
+
+         // green element
+         // Yg = Ag(1-Image->Width)(1-Image->Height) + Bg(Image->Width)(1-Image->Height) + Cg(Image->Height)(1-Image->Width) + Dg(wh)
+         NewImage->PixelData[Offset].g = (a.g)*(1-x_diff)*(1-y_diff) + (b.g)*(x_diff)*(1-y_diff) +
+                                         (c.g)*(y_diff)*(1-x_diff)   + (d.g)*(x_diff*y_diff);
+
+         // red element
+         // Yr = Ar(1-Image->Width)(1-Image->Height) + Br(Image->Width)(1-Image->Height) + Cr(Image->Height)(1-Image->Width) + Dr(wh)
+         NewImage->PixelData[Offset].r = (a.r)*(1-x_diff)*(1-y_diff) + (b.r)*(x_diff)*(1-y_diff) +
+                                         (c.r)*(y_diff)*(1-x_diff)   + (d.r)*(x_diff*y_diff);
+
+         // alpha element
+         NewImage->PixelData[Offset++].a = (a.a)*(1-x_diff)*(1-y_diff) + (b.a)*(x_diff)*(1-y_diff) +
+                                           (c.a)*(y_diff)*(1-x_diff)   + (d.a)*(x_diff*y_diff);
+
+      } // for (j...)
+   } // for (i...)
+   return NewImage;
+} // EG_IMAGE * egScaleImage()
+
 VOID egFreeImage(IN EG_IMAGE *Image)
 {
     if (Image != NULL) {
@@ -260,7 +321,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 +334,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 +345,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,26 +617,26 @@ 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;
-}
+// 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