Passed
Push — develop ( 248214...0e2af0 )
by Jens
02:30
created

BitmapFactory::createImageFromBmp()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 6
rs 9.4285
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
     * Create Image resource from Bitmap
14
     *
15
     * @see       http://www.php.net/manual/en/function.imagecreatefromwbmp.php#86214
16
     * @author    alexander at alexauto dot nl
17
     *
18
     * @param    string $pathToBitmapFile
19
     *
20
     * @return  resource
21
     * @throws \Exception
22
     */
23
    public static function createImageFromBmp($pathToBitmapFile)
24
    {
25
        if (function_exists('imagecreatefrombmp ')) {
26
            return imagecreatefrombmp($pathToBitmapFile);
0 ignored issues
show
Bug introduced by
The function imagecreatefrombmp was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

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