Passed
Push — master ( 3a2d72...a747ff )
by Jens
02:50
created

BitmapFactory::getBitmapFileData()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 3
nop 1
dl 0
loc 12
rs 9.2
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created by: Jens
4
 * Date: 28-3-2018
5
 */
6
7
namespace CloudControl\Cms\images;
8
9
10
class BitmapFactory
11
{
12
13
    /**
14
     * Checks if native function imagecreatefrombmp exists (PHP >= 7.2.0)
15
     * otherwise uses built in implementation
16
     *
17
     * @param $pathToBitmapFile
18
     * @return resource
19
     */
20
    public static function createImageFromBmp($pathToBitmapFile)
21
    {
22
        if (function_exists('imagecreatefrombmp ')) {
23
            return /** @scrutinizer ignore-call */ imagecreatefrombmp($pathToBitmapFile);
24
        }
25
        return self::imageCreateFromBmp($pathToBitmapFile);
26
    }
27
28
    /**
29
     * @param $pathToBitmapFile
30
     * @throws \Exception
31
     * @return bool|string
32
     */
33
    private static function getBitmapFileData($pathToBitmapFile)
34
    {
35
        $fileHandle = fopen($pathToBitmapFile, 'rb');
36
        if ($fileHandle === false) {
37
            throw new \RuntimeException('Could not open bitmapfile ' . $pathToBitmapFile);
38
        }
39
        $bitmapFileData = fread($fileHandle, 10);
40
        while (!feof($fileHandle) && ($bitmapFileData <> "")) {
41
            $bitmapFileData .= fread($fileHandle, 1024);
42
        }
43
44
        return $bitmapFileData;
45
    }
46
47
    /**
48
     * @param string $header
49
     *
50
     * @return array
51
     */
52
    private static function calculateWidthAndHeight($header)
53
    {
54
        $width = null;
55
        $height = null;
56
57
        //    Structure: http://www.fastgraph.com/help/bmp_header_format.html
58
        if (0 === strpos($header, '424d')) {
59
            //    Cut it in parts of 2 bytes
60
            $header_parts = str_split($header, 2);
61
            //    Get the width        4 bytes
62
            $width = hexdec($header_parts[19] . $header_parts[18]);
63
            //    Get the height        4 bytes
64
            $height = hexdec($header_parts[23] . $header_parts[22]);
65
            //    Unset the header params
66
            unset($header_parts);
67
68
            return array($width, $height);
69
        }
70
71
        return array($width, $height);
72
    }
73
74
    /**
75
     * Loop through the data in the body of the bitmap
76
     * file and calculate each individual pixel based on the
77
     * bytes
78
     *
79
     * Using a for-loop with index-calculation instead of str_split to avoid large memory consumption
80
     * Calculate the next DWORD-position in the body
81
     *
82
     * @param BitmapBodyModel $bitmapBodyModel
83
     */
84
    private static function loopThroughBodyAndCalculatePixels(BitmapBodyModel $bitmapBodyModel)
85
    {
86
        for ($i = 0; $i < $bitmapBodyModel->bodySize; $i += 3) {
87
            //    Calculate line-ending and padding
88
            if ($bitmapBodyModel->x >= $bitmapBodyModel->width) {
89
                //    If padding needed, ignore image-padding. Shift i to the ending of the current 32-bit-block
90
                if ($bitmapBodyModel->usePadding) {
91
                    $i += $bitmapBodyModel->width % 4;
92
                }
93
                //    Reset horizontal position
94
                $bitmapBodyModel->x = 0;
95
                //    Raise the height-position (bottom-up)
96
                $bitmapBodyModel->y++;
97
                //    Reached the image-height? Break the for-loop
98
                if ($bitmapBodyModel->y > $bitmapBodyModel->height) {
99
                    break;
100
                }
101
            }
102
            //    Calculation of the RGB-pixel (defined as BGR in image-data). Define $iPos as absolute position in the body
103
            $iPos = $i * 2;
104
            $r = hexdec($bitmapBodyModel->body[$iPos + 4] . $bitmapBodyModel->body[$iPos + 5]);
105
            $g = hexdec($bitmapBodyModel->body[$iPos + 2] . $bitmapBodyModel->body[$iPos + 3]);
106
            $b = hexdec($bitmapBodyModel->body[$iPos] . $bitmapBodyModel->body[$iPos + 1]);
107
            //    Calculate and draw the pixel
108
            $color = imagecolorallocate($bitmapBodyModel->image, (int)$r, (int)$g, (int)$b);
109
            imagesetpixel($bitmapBodyModel->image, $bitmapBodyModel->x, $bitmapBodyModel->height - $bitmapBodyModel->y, $color);
110
            //    Raise the horizontal position
111
            $bitmapBodyModel->x++;
112
        }
113
    }
114
115
    /**
116
     * Create Image resource from Bitmap
117
     *
118
     * @see       http://www.php.net/manual/en/function.imagecreatefromwbmp.php#86214
119
     * @author    alexander at alexauto dot nl
120
     *
121
     * @param    string $pathToBitmapFile
122
     *
123
     * @return  resource
124
     * @throws \Exception
125
     */
126
    public static function imageCreateFromBmp($pathToBitmapFile)
127
    {
128
        $bitmapFileData = self::getBitmapFileData($pathToBitmapFile);
129
130
        $temp = unpack('H*', $bitmapFileData);
131
        $hex = $temp[1];
132
        $header = substr($hex, 0, 108);
133
        list($width, $height) = self::calculateWidthAndHeight($header);
134
135
        //    Define starting X and Y
136
        $x = 0;
137
        $y = 1;
138
139
        $image = imagecreatetruecolor($width, $height);
140
141
        //    Grab the body from the image
142
        $body = substr($hex, 108);
143
144
        //    Calculate if padding at the end-line is needed. Divided by two to keep overview. 1 byte = 2 HEX-chars
145
        $bodySize = (strlen($body) / 2);
146
        $headerSize = ($width * $height);
147
148
        //    Use end-line padding? Only when needed
149
        $usePadding = ($bodySize > ($headerSize * 3) + 4);
150
151
        $bitmapBody = new BitmapBodyModel();
152
        $bitmapBody->bodySize = $bodySize;
153
        $bitmapBody->x = $x;
154
        $bitmapBody->width = $width;
155
        $bitmapBody->usePadding = $usePadding;
156
        $bitmapBody->y = $y;
157
        $bitmapBody->height = $height;
158
        $bitmapBody->body = $body;
159
        $bitmapBody->image = $image;
160
161
        self::loopThroughBodyAndCalculatePixels($bitmapBody);
162
163
        unset($body);
164
165
        return $bitmapBody->image;
166
    }
167
}