Bei einem aktuellen Projekt können sich Benutzer ein Profil anlegen und müssen dazu ein Bild hochladen. Nach dem erfolgreichen Upload werden die Bilder auf eine einheitliche Größe resized und im PNG-Format gespeichert. An sich nichts besonderes. Laut den Anforderungen sollen die Formate JPG, PNG, GIF und BMP unterstüzt werden.
Das letztere Format ist jedoch ein kleiner Fallstrick. So trat der Fall auf, dass ich aus dem BMP kein resized PNG erzeugen konnte. Der Fehler war auch schnell gefunden. Ich nutzte sowohl für BMP wie auch für WBMP die PHP-Funktion imagecreatefromwbmp(). Jedoch steht das W in WBMP nicht etwa für Windows, sondern für Wireless – ein Grafikformat, welches speziell für Mobiltelefone entwickelt wurde. Ein PC-Bitmap kann mir der Funktion nicht verarbeitet werden und PHP stellt keine native Funktion dafür bereit.
Die Lösung findet sich aber glücklicherweise, über mehrere Kommentare verteilt, auf der Doku-Seite zu imagecreatefromwbmp(), welche ich hier nochmal zusammengefasst bereitstellen möchte.
function imagecreatefrombmp($psFile) {
//Load the image into a string
$rFile = fopen($psFile, 'r');
$sRead = fread($rFile, 10);
while (!feof($rFile) && ($sRead != '')) {
$sRead .= fread($rFile, 1024);
}
$aTemp = unpack('H*', $sRead);
$sHex = $aTemp[1];
$sHeader= substr($sHex, 0, 108);
//Process the header
//Structure: http://www.fastgraph.com/help/bmp_header_format.html
if ('424d' == substr($sHeader, 0, 4)) {
//Cut it in parts of 2 bytes
$aHeaderParts = str_split($sHeader, 2);
//Get the width 4 bytes
$iWidth = hexdec($aHeaderParts[19] . $aHeaderParts[18]);
//Get the height 4 bytes
$iHeight = hexdec($aHeaderParts[23] . $aHeaderParts[22]);
//Get the horz. resolution in pixel per meter, 4 bytes
$iDpiX = hexdec($aHeaderParts[39] . $aHeaderParts[38]) * 0.0254;
//Get the vert. resolution in pixel per meter, 4 bytes
$iDpiY = hexdec($aHeaderParts[43] . $aHeaderParts[42]) * 0.0254;
// Unset the header params
unset($aHeaderParts);
}
//Define starting X and Y
$iX = 0;
$iY = 1;
//Create newimage
$rImage = imagecreatetruecolor($iWidth, $iHeight);
//Grab the body from the image
$sBody = substr($sHex, 108);
//Calculate if padding at the end-line is needed divided by two to keep overview.
//1 byte = 2 HEX-chars
$iBodySize = (strlen($sBody) / 2);
$iHeaderSize= ($iWidth * $iHeight);
//Use end-line padding? Only when needed
$iUsePadding = ($iBodySize > ($iHeaderSize * 3) + 4);
//Using a for-loop with index-calculation instaid of str_split to avoid large memory consumption
//Calculate the next DWORD-position in the body
for ($i = 0; $i = $iWidth) {
//If padding needed, ignore image-padding
//Shift i to the ending of the current 32-bit-block
if ($iUsePadding) {
$i += $iWidth % 4;
}
//Reset horizontal position
$iX = 0;
//Raise the height-position (bottom-up)
$iY++;
//Reached the image-height? Break the for-loop
if ($iY > $iHeight) {
break;
}
}
//Calculation of the RGB-pixel (defined as BGR in image-data)
//Define $i_pos as absolute position in the body
$iPos = $i * 2;
$iR = hexdec($sBody[$iPos+4].$sBody[$iPos + 5]);
$iG = hexdec($sBody[$iPos+2].$sBody[$iPos + 3]);
$iB = hexdec($sBody[$iPos] . $sBody[$iPos + 1]);
//Calculate and draw the pixel
$iColor = imagecolorallocate($rImage, $iR, $iG, $iB);
imagesetpixel($rImage, $iX, $iHeight - $iY, $iColor);
//Raise the horizontal position
$iX++;
}
//Unset the body / free the memory
unset($sBody);
//Return image-object
return $rImage;
}
Warum es in PHP eine Funktion für Wireless Bitmaps, aber nicht für die “normalen” PC-Bitmaps gibt, kann ich nicht ganz nachvollziehen.