@shqld/canvas 2.11.2-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +600 -0
- package/binding.gyp +230 -0
- package/browser.js +35 -0
- package/index.js +94 -0
- package/lib/DOMMatrix.js +620 -0
- package/lib/bindings.js +80 -0
- package/lib/canvas.js +113 -0
- package/lib/context2d.js +14 -0
- package/lib/image.js +96 -0
- package/lib/jpegstream.js +41 -0
- package/lib/parse-font.js +101 -0
- package/lib/pattern.js +17 -0
- package/lib/pdfstream.js +35 -0
- package/lib/pngstream.js +42 -0
- package/package.json +71 -0
- package/scripts/install.js +24 -0
- package/src/Backends.cc +18 -0
- package/src/Backends.h +10 -0
- package/src/Canvas.cc +965 -0
- package/src/Canvas.h +96 -0
- package/src/CanvasError.h +23 -0
- package/src/CanvasGradient.cc +123 -0
- package/src/CanvasGradient.h +22 -0
- package/src/CanvasPattern.cc +136 -0
- package/src/CanvasPattern.h +37 -0
- package/src/CanvasRenderingContext2d.cc +3360 -0
- package/src/CanvasRenderingContext2d.h +225 -0
- package/src/Image.cc +1434 -0
- package/src/Image.h +127 -0
- package/src/ImageData.cc +146 -0
- package/src/ImageData.h +27 -0
- package/src/JPEGStream.h +167 -0
- package/src/PNG.h +292 -0
- package/src/Point.h +11 -0
- package/src/Util.h +9 -0
- package/src/backend/Backend.cc +112 -0
- package/src/backend/Backend.h +69 -0
- package/src/backend/ImageBackend.cc +74 -0
- package/src/backend/ImageBackend.h +26 -0
- package/src/backend/PdfBackend.cc +53 -0
- package/src/backend/PdfBackend.h +24 -0
- package/src/backend/SvgBackend.cc +61 -0
- package/src/backend/SvgBackend.h +24 -0
- package/src/bmp/BMPParser.cc +457 -0
- package/src/bmp/BMPParser.h +60 -0
- package/src/bmp/LICENSE.md +24 -0
- package/src/closure.cc +26 -0
- package/src/closure.h +81 -0
- package/src/color.cc +779 -0
- package/src/color.h +30 -0
- package/src/dll_visibility.h +20 -0
- package/src/init.cc +94 -0
- package/src/register_font.cc +408 -0
- package/src/register_font.h +7 -0
- package/types/index.d.ts +484 -0
- package/util/has_lib.js +119 -0
- package/util/win_jpeg_lookup.js +21 -0
package/src/Image.h
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
|
|
2
|
+
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <cairo.h>
|
|
6
|
+
#include "CanvasError.h"
|
|
7
|
+
#include <functional>
|
|
8
|
+
#include <nan.h>
|
|
9
|
+
#include <stdint.h> // node < 7 uses libstdc++ on macOS which lacks complete c++11
|
|
10
|
+
#include <v8.h>
|
|
11
|
+
|
|
12
|
+
#ifdef HAVE_JPEG
|
|
13
|
+
#include <jpeglib.h>
|
|
14
|
+
#include <jerror.h>
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
#ifdef HAVE_GIF
|
|
18
|
+
#include <gif_lib.h>
|
|
19
|
+
|
|
20
|
+
#if GIFLIB_MAJOR > 5 || GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1
|
|
21
|
+
#define GIF_CLOSE_FILE(gif) DGifCloseFile(gif, NULL)
|
|
22
|
+
#else
|
|
23
|
+
#define GIF_CLOSE_FILE(gif) DGifCloseFile(gif)
|
|
24
|
+
#endif
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
#ifdef HAVE_RSVG
|
|
28
|
+
#include <librsvg/rsvg.h>
|
|
29
|
+
// librsvg <= 2.36.1, identified by undefined macro, needs an extra include
|
|
30
|
+
#ifndef LIBRSVG_CHECK_VERSION
|
|
31
|
+
#include <librsvg/rsvg-cairo.h>
|
|
32
|
+
#endif
|
|
33
|
+
#endif
|
|
34
|
+
|
|
35
|
+
using JPEGDecodeL = std::function<uint32_t (uint8_t* const src)>;
|
|
36
|
+
|
|
37
|
+
class Image: public Nan::ObjectWrap {
|
|
38
|
+
public:
|
|
39
|
+
char *filename;
|
|
40
|
+
int width, height;
|
|
41
|
+
int naturalWidth, naturalHeight;
|
|
42
|
+
static Nan::Persistent<v8::FunctionTemplate> constructor;
|
|
43
|
+
static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
|
|
44
|
+
static NAN_METHOD(New);
|
|
45
|
+
static NAN_GETTER(GetComplete);
|
|
46
|
+
static NAN_GETTER(GetWidth);
|
|
47
|
+
static NAN_GETTER(GetHeight);
|
|
48
|
+
static NAN_GETTER(GetNaturalWidth);
|
|
49
|
+
static NAN_GETTER(GetNaturalHeight);
|
|
50
|
+
static NAN_GETTER(GetDataMode);
|
|
51
|
+
static NAN_SETTER(SetDataMode);
|
|
52
|
+
static NAN_SETTER(SetWidth);
|
|
53
|
+
static NAN_SETTER(SetHeight);
|
|
54
|
+
static NAN_METHOD(GetSource);
|
|
55
|
+
static NAN_METHOD(SetSource);
|
|
56
|
+
inline uint8_t *data(){ return cairo_image_surface_get_data(_surface); }
|
|
57
|
+
inline int stride(){ return cairo_image_surface_get_stride(_surface); }
|
|
58
|
+
static int isPNG(uint8_t *data);
|
|
59
|
+
static int isJPEG(uint8_t *data);
|
|
60
|
+
static int isGIF(uint8_t *data);
|
|
61
|
+
static int isSVG(uint8_t *data, unsigned len);
|
|
62
|
+
static int isBMP(uint8_t *data, unsigned len);
|
|
63
|
+
static cairo_status_t readPNG(void *closure, unsigned char *data, unsigned len);
|
|
64
|
+
inline int isComplete(){ return COMPLETE == state; }
|
|
65
|
+
cairo_surface_t *surface();
|
|
66
|
+
cairo_status_t loadSurface();
|
|
67
|
+
cairo_status_t loadFromBuffer(uint8_t *buf, unsigned len);
|
|
68
|
+
cairo_status_t loadPNGFromBuffer(uint8_t *buf);
|
|
69
|
+
cairo_status_t loadPNG();
|
|
70
|
+
void clearData();
|
|
71
|
+
#ifdef HAVE_RSVG
|
|
72
|
+
cairo_status_t loadSVGFromBuffer(uint8_t *buf, unsigned len);
|
|
73
|
+
cairo_status_t loadSVG(FILE *stream);
|
|
74
|
+
cairo_status_t renderSVGToSurface();
|
|
75
|
+
#endif
|
|
76
|
+
#ifdef HAVE_GIF
|
|
77
|
+
cairo_status_t loadGIFFromBuffer(uint8_t *buf, unsigned len);
|
|
78
|
+
cairo_status_t loadGIF(FILE *stream);
|
|
79
|
+
#endif
|
|
80
|
+
#ifdef HAVE_JPEG
|
|
81
|
+
cairo_status_t loadJPEGFromBuffer(uint8_t *buf, unsigned len);
|
|
82
|
+
cairo_status_t loadJPEG(FILE *stream);
|
|
83
|
+
void jpegToARGB(jpeg_decompress_struct* args, uint8_t* data, uint8_t* src, JPEGDecodeL decode);
|
|
84
|
+
cairo_status_t decodeJPEGIntoSurface(jpeg_decompress_struct *info);
|
|
85
|
+
cairo_status_t decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len);
|
|
86
|
+
cairo_status_t assignDataAsMime(uint8_t *data, int len, const char *mime_type);
|
|
87
|
+
#endif
|
|
88
|
+
cairo_status_t loadBMPFromBuffer(uint8_t *buf, unsigned len);
|
|
89
|
+
cairo_status_t loadBMP(FILE *stream);
|
|
90
|
+
CanvasError errorInfo;
|
|
91
|
+
void loaded();
|
|
92
|
+
cairo_status_t load();
|
|
93
|
+
Image();
|
|
94
|
+
|
|
95
|
+
enum {
|
|
96
|
+
DEFAULT
|
|
97
|
+
, LOADING
|
|
98
|
+
, COMPLETE
|
|
99
|
+
} state;
|
|
100
|
+
|
|
101
|
+
enum data_mode_t {
|
|
102
|
+
DATA_IMAGE = 1
|
|
103
|
+
, DATA_MIME = 2
|
|
104
|
+
} data_mode;
|
|
105
|
+
|
|
106
|
+
typedef enum {
|
|
107
|
+
UNKNOWN
|
|
108
|
+
, GIF
|
|
109
|
+
, JPEG
|
|
110
|
+
, PNG
|
|
111
|
+
, SVG
|
|
112
|
+
} type;
|
|
113
|
+
|
|
114
|
+
static type extension(const char *filename);
|
|
115
|
+
|
|
116
|
+
private:
|
|
117
|
+
cairo_surface_t *_surface;
|
|
118
|
+
uint8_t *_data = nullptr;
|
|
119
|
+
int _data_len;
|
|
120
|
+
#ifdef HAVE_RSVG
|
|
121
|
+
RsvgHandle *_rsvg;
|
|
122
|
+
bool _is_svg;
|
|
123
|
+
int _svg_last_width;
|
|
124
|
+
int _svg_last_height;
|
|
125
|
+
#endif
|
|
126
|
+
~Image();
|
|
127
|
+
};
|
package/src/ImageData.cc
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
|
|
2
|
+
|
|
3
|
+
#include "ImageData.h"
|
|
4
|
+
|
|
5
|
+
using namespace v8;
|
|
6
|
+
|
|
7
|
+
Nan::Persistent<FunctionTemplate> ImageData::constructor;
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* Initialize ImageData.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
void
|
|
14
|
+
ImageData::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
|
|
15
|
+
Nan::HandleScope scope;
|
|
16
|
+
|
|
17
|
+
// Constructor
|
|
18
|
+
Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(ImageData::New);
|
|
19
|
+
constructor.Reset(ctor);
|
|
20
|
+
ctor->InstanceTemplate()->SetInternalFieldCount(1);
|
|
21
|
+
ctor->SetClassName(Nan::New("ImageData").ToLocalChecked());
|
|
22
|
+
|
|
23
|
+
// Prototype
|
|
24
|
+
Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
|
|
25
|
+
Nan::SetAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth);
|
|
26
|
+
Nan::SetAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight);
|
|
27
|
+
Local<Context> ctx = Nan::GetCurrentContext();
|
|
28
|
+
Nan::Set(target, Nan::New("ImageData").ToLocalChecked(), ctor->GetFunction(ctx).ToLocalChecked());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
* Initialize a new ImageData object.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
NAN_METHOD(ImageData::New) {
|
|
36
|
+
if (!info.IsConstructCall()) {
|
|
37
|
+
return Nan::ThrowTypeError("Class constructors cannot be invoked without 'new'");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Local<TypedArray> dataArray;
|
|
41
|
+
uint32_t width;
|
|
42
|
+
uint32_t height;
|
|
43
|
+
int length;
|
|
44
|
+
|
|
45
|
+
if (info[0]->IsUint32() && info[1]->IsUint32()) {
|
|
46
|
+
width = Nan::To<uint32_t>(info[0]).FromMaybe(0);
|
|
47
|
+
if (width == 0) {
|
|
48
|
+
Nan::ThrowRangeError("The source width is zero.");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
height = Nan::To<uint32_t>(info[1]).FromMaybe(0);
|
|
52
|
+
if (height == 0) {
|
|
53
|
+
Nan::ThrowRangeError("The source height is zero.");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
length = width * height * 4; // ImageData(w, h) constructor assumes 4 BPP; documented.
|
|
57
|
+
|
|
58
|
+
dataArray = Uint8ClampedArray::New(ArrayBuffer::New(Isolate::GetCurrent(), length), 0, length);
|
|
59
|
+
|
|
60
|
+
} else if (info[0]->IsUint8ClampedArray() && info[1]->IsUint32()) {
|
|
61
|
+
dataArray = info[0].As<Uint8ClampedArray>();
|
|
62
|
+
|
|
63
|
+
length = dataArray->Length();
|
|
64
|
+
if (length == 0) {
|
|
65
|
+
Nan::ThrowRangeError("The input data has a zero byte length.");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Don't assert that the ImageData length is a multiple of four because some
|
|
70
|
+
// data formats are not 4 BPP.
|
|
71
|
+
|
|
72
|
+
width = Nan::To<uint32_t>(info[1]).FromMaybe(0);
|
|
73
|
+
if (width == 0) {
|
|
74
|
+
Nan::ThrowRangeError("The source width is zero.");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Don't assert that the byte length is a multiple of 4 * width, ditto.
|
|
79
|
+
|
|
80
|
+
if (info[2]->IsUint32()) { // Explicit height given
|
|
81
|
+
height = Nan::To<uint32_t>(info[2]).FromMaybe(0);
|
|
82
|
+
} else { // Calculate height assuming 4 BPP
|
|
83
|
+
int size = length / 4;
|
|
84
|
+
height = size / width;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
} else if (info[0]->IsUint16Array() && info[1]->IsUint32()) { // Intended for RGB16_565 format
|
|
88
|
+
dataArray = info[0].As<Uint16Array>();
|
|
89
|
+
|
|
90
|
+
length = dataArray->Length();
|
|
91
|
+
if (length == 0) {
|
|
92
|
+
Nan::ThrowRangeError("The input data has a zero byte length.");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
width = Nan::To<uint32_t>(info[1]).FromMaybe(0);
|
|
97
|
+
if (width == 0) {
|
|
98
|
+
Nan::ThrowRangeError("The source width is zero.");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (info[2]->IsUint32()) { // Explicit height given
|
|
103
|
+
height = Nan::To<uint32_t>(info[2]).FromMaybe(0);
|
|
104
|
+
} else { // Calculate height assuming 2 BPP
|
|
105
|
+
int size = length / 2;
|
|
106
|
+
height = size / width;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
} else {
|
|
110
|
+
Nan::ThrowTypeError("Expected (Uint8ClampedArray, width[, height]), (Uint16Array, width[, height]) or (width, height)");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
Nan::TypedArrayContents<uint8_t> dataPtr(dataArray);
|
|
115
|
+
|
|
116
|
+
ImageData *imageData = new ImageData(reinterpret_cast<uint8_t*>(*dataPtr), width, height);
|
|
117
|
+
imageData->Wrap(info.This());
|
|
118
|
+
Nan::Set(info.This(), Nan::New("data").ToLocalChecked(), dataArray).Check();
|
|
119
|
+
info.GetReturnValue().Set(info.This());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
* Get width.
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
NAN_GETTER(ImageData::GetWidth) {
|
|
127
|
+
if (!ImageData::constructor.Get(info.GetIsolate())->HasInstance(info.This())) {
|
|
128
|
+
Nan::ThrowTypeError("Method ImageData.GetWidth called on incompatible receiver");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
ImageData *imageData = Nan::ObjectWrap::Unwrap<ImageData>(info.This());
|
|
132
|
+
info.GetReturnValue().Set(Nan::New<Number>(imageData->width()));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/*
|
|
136
|
+
* Get height.
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
NAN_GETTER(ImageData::GetHeight) {
|
|
140
|
+
if (!ImageData::constructor.Get(info.GetIsolate())->HasInstance(info.This())) {
|
|
141
|
+
Nan::ThrowTypeError("Method ImageData.GetHeight called on incompatible receiver");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
ImageData *imageData = Nan::ObjectWrap::Unwrap<ImageData>(info.This());
|
|
145
|
+
info.GetReturnValue().Set(Nan::New<Number>(imageData->height()));
|
|
146
|
+
}
|
package/src/ImageData.h
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
|
|
2
|
+
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <nan.h>
|
|
6
|
+
#include <stdint.h> // node < 7 uses libstdc++ on macOS which lacks complete c++11
|
|
7
|
+
#include <v8.h>
|
|
8
|
+
|
|
9
|
+
class ImageData: public Nan::ObjectWrap {
|
|
10
|
+
public:
|
|
11
|
+
static Nan::Persistent<v8::FunctionTemplate> constructor;
|
|
12
|
+
static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
|
|
13
|
+
static NAN_METHOD(New);
|
|
14
|
+
static NAN_GETTER(GetWidth);
|
|
15
|
+
static NAN_GETTER(GetHeight);
|
|
16
|
+
|
|
17
|
+
inline int width() { return _width; }
|
|
18
|
+
inline int height() { return _height; }
|
|
19
|
+
inline uint8_t *data() { return _data; }
|
|
20
|
+
ImageData(uint8_t *data, int width, int height) : _width(width), _height(height), _data(data) {}
|
|
21
|
+
|
|
22
|
+
private:
|
|
23
|
+
int _width;
|
|
24
|
+
int _height;
|
|
25
|
+
uint8_t *_data;
|
|
26
|
+
|
|
27
|
+
};
|
package/src/JPEGStream.h
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "closure.h"
|
|
4
|
+
#include <jpeglib.h>
|
|
5
|
+
#include <jerror.h>
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* Expanded data destination object for closure output,
|
|
9
|
+
* inspired by IJG's jdatadst.c
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
struct closure_destination_mgr {
|
|
13
|
+
jpeg_destination_mgr pub;
|
|
14
|
+
JpegClosure* closure;
|
|
15
|
+
JOCTET *buffer;
|
|
16
|
+
int bufsize;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
void
|
|
20
|
+
init_closure_destination(j_compress_ptr cinfo){
|
|
21
|
+
// we really don't have to do anything here
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
boolean
|
|
25
|
+
empty_closure_output_buffer(j_compress_ptr cinfo){
|
|
26
|
+
Nan::HandleScope scope;
|
|
27
|
+
Nan::AsyncResource async("canvas:empty_closure_output_buffer");
|
|
28
|
+
closure_destination_mgr *dest = (closure_destination_mgr *) cinfo->dest;
|
|
29
|
+
|
|
30
|
+
v8::Local<v8::Object> buf = Nan::NewBuffer((char *)dest->buffer, dest->bufsize).ToLocalChecked();
|
|
31
|
+
|
|
32
|
+
// emit "data"
|
|
33
|
+
v8::Local<v8::Value> argv[2] = {
|
|
34
|
+
Nan::Null()
|
|
35
|
+
, buf
|
|
36
|
+
};
|
|
37
|
+
dest->closure->cb.Call(sizeof argv / sizeof *argv, argv, &async);
|
|
38
|
+
|
|
39
|
+
dest->buffer = (JOCTET *)malloc(dest->bufsize);
|
|
40
|
+
cinfo->dest->next_output_byte = dest->buffer;
|
|
41
|
+
cinfo->dest->free_in_buffer = dest->bufsize;
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
void
|
|
46
|
+
term_closure_destination(j_compress_ptr cinfo){
|
|
47
|
+
Nan::HandleScope scope;
|
|
48
|
+
Nan::AsyncResource async("canvas:term_closure_destination");
|
|
49
|
+
closure_destination_mgr *dest = (closure_destination_mgr *) cinfo->dest;
|
|
50
|
+
|
|
51
|
+
/* emit remaining data */
|
|
52
|
+
v8::Local<v8::Object> buf = Nan::NewBuffer((char *)dest->buffer, dest->bufsize - dest->pub.free_in_buffer).ToLocalChecked();
|
|
53
|
+
|
|
54
|
+
v8::Local<v8::Value> data_argv[2] = {
|
|
55
|
+
Nan::Null()
|
|
56
|
+
, buf
|
|
57
|
+
};
|
|
58
|
+
dest->closure->cb.Call(sizeof data_argv / sizeof *data_argv, data_argv, &async);
|
|
59
|
+
|
|
60
|
+
// emit "end"
|
|
61
|
+
v8::Local<v8::Value> end_argv[2] = {
|
|
62
|
+
Nan::Null()
|
|
63
|
+
, Nan::Null()
|
|
64
|
+
};
|
|
65
|
+
dest->closure->cb.Call(sizeof end_argv / sizeof *end_argv, end_argv, &async);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
void
|
|
69
|
+
jpeg_closure_dest(j_compress_ptr cinfo, JpegClosure* closure, int bufsize){
|
|
70
|
+
closure_destination_mgr * dest;
|
|
71
|
+
|
|
72
|
+
/* The destination object is made permanent so that multiple JPEG images
|
|
73
|
+
* can be written to the same buffer without re-executing jpeg_mem_dest.
|
|
74
|
+
*/
|
|
75
|
+
if (cinfo->dest == NULL) { /* first time for this JPEG object? */
|
|
76
|
+
cinfo->dest = (struct jpeg_destination_mgr *)
|
|
77
|
+
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
|
|
78
|
+
sizeof(closure_destination_mgr));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
dest = (closure_destination_mgr *) cinfo->dest;
|
|
82
|
+
|
|
83
|
+
cinfo->dest->init_destination = &init_closure_destination;
|
|
84
|
+
cinfo->dest->empty_output_buffer = &empty_closure_output_buffer;
|
|
85
|
+
cinfo->dest->term_destination = &term_closure_destination;
|
|
86
|
+
|
|
87
|
+
dest->closure = closure;
|
|
88
|
+
dest->bufsize = bufsize;
|
|
89
|
+
dest->buffer = (JOCTET *)malloc(bufsize);
|
|
90
|
+
|
|
91
|
+
cinfo->dest->next_output_byte = dest->buffer;
|
|
92
|
+
cinfo->dest->free_in_buffer = dest->bufsize;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
void encode_jpeg(jpeg_compress_struct cinfo, cairo_surface_t *surface, int quality, bool progressive, int chromaHSampFactor, int chromaVSampFactor) {
|
|
96
|
+
int w = cairo_image_surface_get_width(surface);
|
|
97
|
+
int h = cairo_image_surface_get_height(surface);
|
|
98
|
+
|
|
99
|
+
cinfo.in_color_space = JCS_RGB;
|
|
100
|
+
cinfo.input_components = 3;
|
|
101
|
+
cinfo.image_width = w;
|
|
102
|
+
cinfo.image_height = h;
|
|
103
|
+
jpeg_set_defaults(&cinfo);
|
|
104
|
+
if (progressive)
|
|
105
|
+
jpeg_simple_progression(&cinfo);
|
|
106
|
+
jpeg_set_quality(&cinfo, quality, (quality < 25) ? 0 : 1);
|
|
107
|
+
cinfo.comp_info[0].h_samp_factor = chromaHSampFactor;
|
|
108
|
+
cinfo.comp_info[0].v_samp_factor = chromaVSampFactor;
|
|
109
|
+
|
|
110
|
+
JSAMPROW slr;
|
|
111
|
+
jpeg_start_compress(&cinfo, TRUE);
|
|
112
|
+
unsigned char *dst;
|
|
113
|
+
unsigned int *src = (unsigned int *)cairo_image_surface_get_data(surface);
|
|
114
|
+
int sl = 0;
|
|
115
|
+
dst = (unsigned char *)malloc(w * 3);
|
|
116
|
+
while (sl < h) {
|
|
117
|
+
unsigned char *dp = dst;
|
|
118
|
+
int x = 0;
|
|
119
|
+
while (x < w) {
|
|
120
|
+
dp[0] = (*src >> 16) & 255;
|
|
121
|
+
dp[1] = (*src >> 8) & 255;
|
|
122
|
+
dp[2] = *src & 255;
|
|
123
|
+
src++;
|
|
124
|
+
dp += 3;
|
|
125
|
+
x++;
|
|
126
|
+
}
|
|
127
|
+
slr = dst;
|
|
128
|
+
jpeg_write_scanlines(&cinfo, &slr, 1);
|
|
129
|
+
sl++;
|
|
130
|
+
}
|
|
131
|
+
free(dst);
|
|
132
|
+
jpeg_finish_compress(&cinfo);
|
|
133
|
+
jpeg_destroy_compress(&cinfo);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
void
|
|
137
|
+
write_to_jpeg_stream(cairo_surface_t *surface, int bufsize, JpegClosure* closure) {
|
|
138
|
+
jpeg_compress_struct cinfo;
|
|
139
|
+
jpeg_error_mgr jerr;
|
|
140
|
+
cinfo.err = jpeg_std_error(&jerr);
|
|
141
|
+
jpeg_create_compress(&cinfo);
|
|
142
|
+
jpeg_closure_dest(&cinfo, closure, bufsize);
|
|
143
|
+
encode_jpeg(
|
|
144
|
+
cinfo,
|
|
145
|
+
surface,
|
|
146
|
+
closure->quality,
|
|
147
|
+
closure->progressive,
|
|
148
|
+
closure->chromaSubsampling,
|
|
149
|
+
closure->chromaSubsampling);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
void
|
|
153
|
+
write_to_jpeg_buffer(cairo_surface_t* surface, JpegClosure* closure) {
|
|
154
|
+
jpeg_compress_struct cinfo;
|
|
155
|
+
jpeg_error_mgr jerr;
|
|
156
|
+
cinfo.err = jpeg_std_error(&jerr);
|
|
157
|
+
jpeg_create_compress(&cinfo);
|
|
158
|
+
cinfo.client_data = closure;
|
|
159
|
+
cinfo.dest = closure->jpeg_dest_mgr;
|
|
160
|
+
encode_jpeg(
|
|
161
|
+
cinfo,
|
|
162
|
+
surface,
|
|
163
|
+
closure->quality,
|
|
164
|
+
closure->progressive,
|
|
165
|
+
closure->chromaSubsampling,
|
|
166
|
+
closure->chromaSubsampling);
|
|
167
|
+
}
|