Initial Commit
This commit is contained in:
457
node_modules/canvas/src/bmp/BMPParser.cc
generated
vendored
Normal file
457
node_modules/canvas/src/bmp/BMPParser.cc
generated
vendored
Normal file
@@ -0,0 +1,457 @@
|
||||
#include "BMPParser.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace std;
|
||||
using namespace BMPParser;
|
||||
|
||||
#define MAX_IMG_SIZE 10000
|
||||
|
||||
#define E(cond, msg) if(cond) return setErr(msg)
|
||||
#define EU(cond, msg) if(cond) return setErrUnsupported(msg)
|
||||
#define EX(cond, msg) if(cond) return setErrUnknown(msg)
|
||||
|
||||
#define I1() get<char>()
|
||||
#define U1() get<uint8_t>()
|
||||
#define I2() get<int16_t>()
|
||||
#define U2() get<uint16_t>()
|
||||
#define I4() get<int32_t>()
|
||||
#define U4() get<uint32_t>()
|
||||
|
||||
#define I1UC() get<char, false>()
|
||||
#define U1UC() get<uint8_t, false>()
|
||||
#define I2UC() get<int16_t, false>()
|
||||
#define U2UC() get<uint16_t, false>()
|
||||
#define I4UC() get<int32_t, false>()
|
||||
#define U4UC() get<uint32_t, false>()
|
||||
|
||||
#define CHECK_OVERRUN(ptr, size, type) \
|
||||
if((ptr) + (size) - data > len){ \
|
||||
setErr("unexpected end of file"); \
|
||||
return type(); \
|
||||
}
|
||||
|
||||
Parser::~Parser(){
|
||||
data = nullptr;
|
||||
ptr = nullptr;
|
||||
|
||||
if(imgd){
|
||||
delete[] imgd;
|
||||
imgd = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::parse(uint8_t *buf, int bufSize, uint8_t *format){
|
||||
assert(status == Status::EMPTY);
|
||||
|
||||
data = ptr = buf;
|
||||
len = bufSize;
|
||||
|
||||
// Start parsing file header
|
||||
setOp("file header");
|
||||
|
||||
// File header signature
|
||||
string fhSig = getStr(2);
|
||||
string temp = "file header signature";
|
||||
EU(fhSig == "BA", temp + " \"BA\"");
|
||||
EU(fhSig == "CI", temp + " \"CI\"");
|
||||
EU(fhSig == "CP", temp + " \"CP\"");
|
||||
EU(fhSig == "IC", temp + " \"IC\"");
|
||||
EU(fhSig == "PT", temp + " \"PT\"");
|
||||
EX(fhSig != "BM", temp); // BM
|
||||
|
||||
// Length of the file should not be larger than `len`
|
||||
E(U4() > static_cast<uint32_t>(len), "inconsistent file size");
|
||||
|
||||
// Skip unused values
|
||||
skip(4);
|
||||
|
||||
// Offset where the pixel array (bitmap data) can be found
|
||||
auto imgdOffset = U4();
|
||||
|
||||
// Start parsing DIB header
|
||||
setOp("DIB header");
|
||||
|
||||
// Prepare some variables in case they are needed
|
||||
uint32_t compr = 0;
|
||||
uint32_t redShift = 0, greenShift = 0, blueShift = 0, alphaShift = 0;
|
||||
uint32_t redMask = 0, greenMask = 0, blueMask = 0, alphaMask = 0;
|
||||
double redMultp = 0, greenMultp = 0, blueMultp = 0, alphaMultp = 0;
|
||||
|
||||
/**
|
||||
* Type of the DIB (device-independent bitmap) header
|
||||
* is determined by its size. Most BMP files use BITMAPINFOHEADER.
|
||||
*/
|
||||
auto dibSize = U4();
|
||||
temp = "DIB header";
|
||||
EU(dibSize == 64, temp + " \"OS22XBITMAPHEADER\"");
|
||||
EU(dibSize == 16, temp + " \"OS22XBITMAPHEADER\"");
|
||||
|
||||
uint32_t infoHeader = dibSize == 40 ? 1 :
|
||||
dibSize == 52 ? 2 :
|
||||
dibSize == 56 ? 3 :
|
||||
dibSize == 108 ? 4 :
|
||||
dibSize == 124 ? 5 : 0;
|
||||
|
||||
// BITMAPCOREHEADER, BITMAP*INFOHEADER, BITMAP*HEADER
|
||||
auto isDibValid = dibSize == 12 || infoHeader;
|
||||
EX(!isDibValid, temp);
|
||||
|
||||
// Image width
|
||||
w = dibSize == 12 ? U2() : I4();
|
||||
E(!w, "image width is 0");
|
||||
E(w < 0, "negative image width");
|
||||
E(w > MAX_IMG_SIZE, "too large image width");
|
||||
|
||||
// Image height (specification allows negative values)
|
||||
h = dibSize == 12 ? U2() : I4();
|
||||
E(!h, "image height is 0");
|
||||
E(h > MAX_IMG_SIZE, "too large image height");
|
||||
|
||||
bool isHeightNegative = h < 0;
|
||||
if(isHeightNegative) h = -h;
|
||||
|
||||
// Number of color planes (must be 1)
|
||||
E(U2() != 1, "number of color planes must be 1");
|
||||
|
||||
// Bits per pixel (color depth)
|
||||
auto bpp = U2();
|
||||
auto isBppValid = bpp == 1 || bpp == 4 || bpp == 8 || bpp == 16 || bpp == 24 || bpp == 32;
|
||||
EU(!isBppValid, "color depth");
|
||||
|
||||
// Calculate image data size and padding
|
||||
uint32_t expectedImgdSize = (((w * bpp + 31) >> 5) << 2) * h;
|
||||
uint32_t rowPadding = (-w * bpp & 31) >> 3;
|
||||
uint32_t imgdSize = 0;
|
||||
|
||||
// Color palette data
|
||||
uint8_t* paletteStart = nullptr;
|
||||
uint32_t palColNum = 0;
|
||||
|
||||
if(infoHeader){
|
||||
// Compression type
|
||||
compr = U4();
|
||||
temp = "compression type";
|
||||
EU(compr == 1, temp + " \"BI_RLE8\"");
|
||||
EU(compr == 2, temp + " \"BI_RLE4\"");
|
||||
EU(compr == 4, temp + " \"BI_JPEG\"");
|
||||
EU(compr == 5, temp + " \"BI_PNG\"");
|
||||
EU(compr == 6, temp + " \"BI_ALPHABITFIELDS\"");
|
||||
EU(compr == 11, temp + " \"BI_CMYK\"");
|
||||
EU(compr == 12, temp + " \"BI_CMYKRLE8\"");
|
||||
EU(compr == 13, temp + " \"BI_CMYKRLE4\"");
|
||||
|
||||
// BI_RGB and BI_BITFIELDS
|
||||
auto isComprValid = compr == 0 || compr == 3;
|
||||
EX(!isComprValid, temp);
|
||||
|
||||
// Ensure that BI_BITFIELDS appears only with 16-bit or 32-bit color
|
||||
E(compr == 3 && !(bpp == 16 || bpp == 32), "compression BI_BITFIELDS can be used only with 16-bit and 32-bit color depth");
|
||||
|
||||
// Size of the image data
|
||||
imgdSize = U4();
|
||||
|
||||
// Horizontal and vertical resolution (ignored)
|
||||
skip(8);
|
||||
|
||||
// Number of colors in the palette or 0 if no palette is present
|
||||
palColNum = U4();
|
||||
EU(palColNum && bpp > 8, "color palette and bit depth combination");
|
||||
if(palColNum) paletteStart = data + dibSize + 14;
|
||||
|
||||
// Number of important colors used or 0 if all colors are important (generally ignored)
|
||||
skip(4);
|
||||
|
||||
if(infoHeader >= 2){
|
||||
// If BI_BITFIELDS are used, calculate masks, otherwise ignore them
|
||||
if(compr == 3){
|
||||
calcMaskShift(redShift, redMask, redMultp);
|
||||
calcMaskShift(greenShift, greenMask, greenMultp);
|
||||
calcMaskShift(blueShift, blueMask, blueMultp);
|
||||
if(infoHeader >= 3) calcMaskShift(alphaShift, alphaMask, alphaMultp);
|
||||
if(status == Status::ERROR) return;
|
||||
}else{
|
||||
skip(16);
|
||||
}
|
||||
|
||||
// Ensure that the color space is LCS_WINDOWS_COLOR_SPACE or sRGB
|
||||
if(infoHeader >= 4 && !palColNum){
|
||||
string colSpace = getStr(4, 1);
|
||||
EU(colSpace != "Win " && colSpace != "sRGB", "color space \"" + colSpace + "\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip to the image data (there may be other chunks between, but they are optional)
|
||||
E(ptr - data > imgdOffset, "image data overlaps with another structure");
|
||||
ptr = data + imgdOffset;
|
||||
|
||||
// Start parsing image data
|
||||
setOp("image data");
|
||||
|
||||
if(!imgdSize){
|
||||
// Value 0 is allowed only for BI_RGB compression type
|
||||
E(compr != 0, "missing image data size");
|
||||
imgdSize = expectedImgdSize;
|
||||
}else{
|
||||
E(imgdSize < expectedImgdSize, "invalid image data size");
|
||||
}
|
||||
|
||||
// Ensure that all image data is present
|
||||
E(ptr - data + imgdSize > len, "not enough image data");
|
||||
|
||||
// Direction of reading rows
|
||||
int yStart = h - 1;
|
||||
int yEnd = -1;
|
||||
int dy = isHeightNegative ? 1 : -1;
|
||||
|
||||
// In case of negative height, read rows backward
|
||||
if(isHeightNegative){
|
||||
yStart = 0;
|
||||
yEnd = h;
|
||||
}
|
||||
|
||||
// Allocate output image data array
|
||||
int buffLen = w * h << 2;
|
||||
imgd = new (nothrow) uint8_t[buffLen];
|
||||
E(!imgd, "unable to allocate memory");
|
||||
|
||||
// Prepare color values
|
||||
uint8_t color[4] = {0};
|
||||
uint8_t &red = color[0];
|
||||
uint8_t &green = color[1];
|
||||
uint8_t &blue = color[2];
|
||||
uint8_t &alpha = color[3];
|
||||
|
||||
// Check if pre-multiplied alpha is used
|
||||
bool premul = format ? format[4] : 0;
|
||||
|
||||
// Main loop
|
||||
for(int y = yStart; y != yEnd; y += dy){
|
||||
// Use in-byte offset for bpp < 8
|
||||
uint8_t colOffset = 0;
|
||||
uint8_t cval = 0;
|
||||
uint32_t val = 0;
|
||||
|
||||
for(int x = 0; x != w; x++){
|
||||
// Index in the output image data
|
||||
int i = (x + y * w) << 2;
|
||||
|
||||
switch(compr){
|
||||
case 0: // BI_RGB
|
||||
switch(bpp){
|
||||
case 1:
|
||||
if(colOffset) ptr--;
|
||||
cval = (U1UC() >> (7 - colOffset)) & 1;
|
||||
|
||||
if(palColNum){
|
||||
uint8_t* entry = paletteStart + (cval << 2);
|
||||
blue = get<uint8_t>(entry);
|
||||
green = get<uint8_t>(entry + 1);
|
||||
red = get<uint8_t>(entry + 2);
|
||||
if(status == Status::ERROR) return;
|
||||
}else{
|
||||
red = green = blue = cval ? 255 : 0;
|
||||
}
|
||||
|
||||
alpha = 255;
|
||||
colOffset = (colOffset + 1) & 7;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if(colOffset) ptr--;
|
||||
cval = (U1UC() >> (4 - colOffset)) & 15;
|
||||
|
||||
if(palColNum){
|
||||
uint8_t* entry = paletteStart + (cval << 2);
|
||||
blue = get<uint8_t>(entry);
|
||||
green = get<uint8_t>(entry + 1);
|
||||
red = get<uint8_t>(entry + 2);
|
||||
if(status == Status::ERROR) return;
|
||||
}else{
|
||||
red = green = blue = cval << 4;
|
||||
}
|
||||
|
||||
alpha = 255;
|
||||
colOffset = (colOffset + 4) & 7;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
cval = U1UC();
|
||||
|
||||
if(palColNum){
|
||||
uint8_t* entry = paletteStart + (cval << 2);
|
||||
blue = get<uint8_t>(entry);
|
||||
green = get<uint8_t>(entry + 1);
|
||||
red = get<uint8_t>(entry + 2);
|
||||
if(status == Status::ERROR) return;
|
||||
}else{
|
||||
red = green = blue = cval;
|
||||
}
|
||||
|
||||
alpha = 255;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
// RGB555
|
||||
val = U1UC();
|
||||
val |= U1UC() << 8;
|
||||
red = (val >> 10) << 3;
|
||||
green = (val >> 5) << 3;
|
||||
blue = val << 3;
|
||||
alpha = 255;
|
||||
break;
|
||||
|
||||
case 24:
|
||||
blue = U1UC();
|
||||
green = U1UC();
|
||||
red = U1UC();
|
||||
alpha = 255;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
blue = U1UC();
|
||||
green = U1UC();
|
||||
red = U1UC();
|
||||
|
||||
if(infoHeader >= 3){
|
||||
alpha = U1UC();
|
||||
}else{
|
||||
alpha = 255;
|
||||
skip(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // BI_BITFIELDS
|
||||
uint32_t col = bpp == 16 ? U2UC() : U4UC();
|
||||
red = ((col >> redShift) & redMask) * redMultp + .5;
|
||||
green = ((col >> greenShift) & greenMask) * greenMultp + .5;
|
||||
blue = ((col >> blueShift) & blueMask) * blueMultp + .5;
|
||||
alpha = alphaMask ? ((col >> alphaShift) & alphaMask) * alphaMultp + .5 : 255;
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pixel format:
|
||||
* red,
|
||||
* green,
|
||||
* blue,
|
||||
* alpha,
|
||||
* is alpha pre-multiplied
|
||||
* Default is [0, 1, 2, 3, 0]
|
||||
*/
|
||||
|
||||
if(premul && alpha != 255){
|
||||
double a = alpha / 255.;
|
||||
red = static_cast<uint8_t>(red * a + .5);
|
||||
green = static_cast<uint8_t>(green * a + .5);
|
||||
blue = static_cast<uint8_t>(blue * a + .5);
|
||||
}
|
||||
|
||||
if(format){
|
||||
imgd[i] = color[format[0]];
|
||||
imgd[i + 1] = color[format[1]];
|
||||
imgd[i + 2] = color[format[2]];
|
||||
imgd[i + 3] = color[format[3]];
|
||||
}else{
|
||||
imgd[i] = red;
|
||||
imgd[i + 1] = green;
|
||||
imgd[i + 2] = blue;
|
||||
imgd[i + 3] = alpha;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip unused bytes in the current row
|
||||
skip(rowPadding);
|
||||
}
|
||||
|
||||
if(status == Status::ERROR) return;
|
||||
status = Status::OK;
|
||||
};
|
||||
|
||||
void Parser::clearImgd(){ imgd = nullptr; }
|
||||
int32_t Parser::getWidth() const{ return w; }
|
||||
int32_t Parser::getHeight() const{ return h; }
|
||||
uint8_t *Parser::getImgd() const{ return imgd; }
|
||||
Status Parser::getStatus() const{ return status; }
|
||||
|
||||
string Parser::getErrMsg() const{
|
||||
return "Error while processing " + getOp() + " - " + err;
|
||||
}
|
||||
|
||||
template <typename T, bool check> inline T Parser::get(){
|
||||
if(check)
|
||||
CHECK_OVERRUN(ptr, sizeof(T), T);
|
||||
T val = *(T*)ptr;
|
||||
ptr += sizeof(T);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T, bool check> inline T Parser::get(uint8_t* pointer){
|
||||
if(check)
|
||||
CHECK_OVERRUN(pointer, sizeof(T), T);
|
||||
T val = *(T*)pointer;
|
||||
return val;
|
||||
}
|
||||
|
||||
string Parser::getStr(int size, bool reverse){
|
||||
CHECK_OVERRUN(ptr, size, string);
|
||||
string val = "";
|
||||
|
||||
while(size--){
|
||||
if(reverse) val = string(1, static_cast<char>(*ptr++)) + val;
|
||||
else val += static_cast<char>(*ptr++);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
inline void Parser::skip(int size){
|
||||
CHECK_OVERRUN(ptr, size, void);
|
||||
ptr += size;
|
||||
}
|
||||
|
||||
void Parser::calcMaskShift(uint32_t& shift, uint32_t& mask, double& multp){
|
||||
mask = U4();
|
||||
shift = 0;
|
||||
|
||||
if(mask == 0) return;
|
||||
|
||||
while(~mask & 1){
|
||||
mask >>= 1;
|
||||
shift++;
|
||||
}
|
||||
|
||||
E(mask & (mask + 1), "invalid color mask");
|
||||
|
||||
multp = 255. / mask;
|
||||
}
|
||||
|
||||
void Parser::setOp(string val){
|
||||
if(status != Status::EMPTY) return;
|
||||
op = val;
|
||||
}
|
||||
|
||||
string Parser::getOp() const{
|
||||
return op;
|
||||
}
|
||||
|
||||
void Parser::setErrUnsupported(string msg){
|
||||
setErr("unsupported " + msg);
|
||||
}
|
||||
|
||||
void Parser::setErrUnknown(string msg){
|
||||
setErr("unknown " + msg);
|
||||
}
|
||||
|
||||
void Parser::setErr(string msg){
|
||||
if(status != Status::EMPTY) return;
|
||||
err = msg;
|
||||
status = Status::ERROR;
|
||||
}
|
||||
|
||||
string Parser::getErr() const{
|
||||
return err;
|
||||
}
|
||||
60
node_modules/canvas/src/bmp/BMPParser.h
generated
vendored
Normal file
60
node_modules/canvas/src/bmp/BMPParser.h
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ERROR
|
||||
#define ERROR_ ERROR
|
||||
#undef ERROR
|
||||
#endif
|
||||
|
||||
#include <stdint.h> // node < 7 uses libstdc++ on macOS which lacks complete c++11
|
||||
#include <string>
|
||||
|
||||
namespace BMPParser{
|
||||
enum Status{
|
||||
EMPTY,
|
||||
OK,
|
||||
ERROR,
|
||||
};
|
||||
|
||||
class Parser{
|
||||
public:
|
||||
Parser()=default;
|
||||
~Parser();
|
||||
void parse(uint8_t *buf, int bufSize, uint8_t *format=nullptr);
|
||||
void clearImgd();
|
||||
int32_t getWidth() const;
|
||||
int32_t getHeight() const;
|
||||
uint8_t *getImgd() const;
|
||||
Status getStatus() const;
|
||||
std::string getErrMsg() const;
|
||||
|
||||
private:
|
||||
Status status = Status::EMPTY;
|
||||
uint8_t *data = nullptr;
|
||||
uint8_t *ptr = nullptr;
|
||||
int len = 0;
|
||||
int32_t w = 0;
|
||||
int32_t h = 0;
|
||||
uint8_t *imgd = nullptr;
|
||||
std::string err = "";
|
||||
std::string op = "";
|
||||
|
||||
template <typename T, bool check=true> inline T get();
|
||||
template <typename T, bool check=true> inline T get(uint8_t* pointer);
|
||||
std::string getStr(int len, bool reverse=false);
|
||||
inline void skip(int len);
|
||||
void calcMaskShift(uint32_t& shift, uint32_t& mask, double& multp);
|
||||
|
||||
void setOp(std::string val);
|
||||
std::string getOp() const;
|
||||
|
||||
void setErrUnsupported(std::string msg);
|
||||
void setErrUnknown(std::string msg);
|
||||
void setErr(std::string msg);
|
||||
std::string getErr() const;
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef ERROR_
|
||||
#define ERROR ERROR_
|
||||
#undef ERROR_
|
||||
#endif
|
||||
24
node_modules/canvas/src/bmp/LICENSE.md
generated
vendored
Normal file
24
node_modules/canvas/src/bmp/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
||||
Reference in New Issue
Block a user