@shqld/canvas 3.2.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 +654 -0
- package/binding.gyp +229 -0
- package/browser.js +31 -0
- package/index.d.ts +507 -0
- package/index.js +94 -0
- package/lib/DOMMatrix.js +678 -0
- package/lib/bindings.js +113 -0
- package/lib/canvas.js +113 -0
- package/lib/context2d.js +11 -0
- package/lib/image.js +97 -0
- package/lib/jpegstream.js +41 -0
- package/lib/pattern.js +15 -0
- package/lib/pdfstream.js +35 -0
- package/lib/pngstream.js +42 -0
- package/package.json +77 -0
- package/scripts/install.js +19 -0
- package/src/Backends.h +9 -0
- package/src/Canvas.cc +1026 -0
- package/src/Canvas.h +128 -0
- package/src/CanvasError.h +37 -0
- package/src/CanvasGradient.cc +113 -0
- package/src/CanvasGradient.h +20 -0
- package/src/CanvasPattern.cc +129 -0
- package/src/CanvasPattern.h +33 -0
- package/src/CanvasRenderingContext2d.cc +3527 -0
- package/src/CanvasRenderingContext2d.h +238 -0
- package/src/CharData.h +233 -0
- package/src/FontParser.cc +605 -0
- package/src/FontParser.h +115 -0
- package/src/Image.cc +1719 -0
- package/src/Image.h +146 -0
- package/src/ImageData.cc +138 -0
- package/src/ImageData.h +26 -0
- package/src/InstanceData.h +12 -0
- package/src/JPEGStream.h +157 -0
- package/src/PNG.h +292 -0
- package/src/Point.h +11 -0
- package/src/Util.h +9 -0
- package/src/bmp/BMPParser.cc +459 -0
- package/src/bmp/BMPParser.h +60 -0
- package/src/bmp/LICENSE.md +24 -0
- package/src/closure.cc +52 -0
- package/src/closure.h +98 -0
- package/src/color.cc +796 -0
- package/src/color.h +30 -0
- package/src/dll_visibility.h +20 -0
- package/src/init.cc +114 -0
- package/src/register_font.cc +352 -0
- package/src/register_font.h +7 -0
- package/util/has_lib.js +119 -0
- package/util/win_jpeg_lookup.js +21 -0
package/src/Image.h
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
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 <napi.h>
|
|
9
|
+
#include <stdint.h> // node < 7 uses libstdc++ on macOS which lacks complete c++11
|
|
10
|
+
|
|
11
|
+
#ifdef HAVE_JPEG
|
|
12
|
+
#include <jpeglib.h>
|
|
13
|
+
#include <jerror.h>
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
#ifdef HAVE_GIF
|
|
17
|
+
#include <gif_lib.h>
|
|
18
|
+
|
|
19
|
+
#if GIFLIB_MAJOR > 5 || GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1
|
|
20
|
+
#define GIF_CLOSE_FILE(gif) DGifCloseFile(gif, NULL)
|
|
21
|
+
#else
|
|
22
|
+
#define GIF_CLOSE_FILE(gif) DGifCloseFile(gif)
|
|
23
|
+
#endif
|
|
24
|
+
#endif
|
|
25
|
+
|
|
26
|
+
#ifdef HAVE_RSVG
|
|
27
|
+
#include <librsvg/rsvg.h>
|
|
28
|
+
// librsvg <= 2.36.1, identified by undefined macro, needs an extra include
|
|
29
|
+
#ifndef LIBRSVG_CHECK_VERSION
|
|
30
|
+
#include <librsvg/rsvg-cairo.h>
|
|
31
|
+
#endif
|
|
32
|
+
#endif
|
|
33
|
+
|
|
34
|
+
using JPEGDecodeL = std::function<uint32_t (uint8_t* const src)>;
|
|
35
|
+
|
|
36
|
+
class Image : public Napi::ObjectWrap<Image> {
|
|
37
|
+
public:
|
|
38
|
+
char *filename;
|
|
39
|
+
int width, height;
|
|
40
|
+
int naturalWidth, naturalHeight;
|
|
41
|
+
Napi::Env env;
|
|
42
|
+
static Napi::FunctionReference constructor;
|
|
43
|
+
static void Initialize(Napi::Env& env, Napi::Object& target);
|
|
44
|
+
Image(const Napi::CallbackInfo& info);
|
|
45
|
+
Napi::Value GetComplete(const Napi::CallbackInfo& info);
|
|
46
|
+
Napi::Value GetWidth(const Napi::CallbackInfo& info);
|
|
47
|
+
Napi::Value GetHeight(const Napi::CallbackInfo& info);
|
|
48
|
+
Napi::Value GetNaturalWidth(const Napi::CallbackInfo& info);
|
|
49
|
+
Napi::Value GetNaturalHeight(const Napi::CallbackInfo& info);
|
|
50
|
+
Napi::Value GetDataMode(const Napi::CallbackInfo& info);
|
|
51
|
+
void SetDataMode(const Napi::CallbackInfo& info, const Napi::Value& value);
|
|
52
|
+
void SetWidth(const Napi::CallbackInfo& info, const Napi::Value& value);
|
|
53
|
+
void SetHeight(const Napi::CallbackInfo& info, const Napi::Value& value);
|
|
54
|
+
static Napi::Value GetSource(const Napi::CallbackInfo& info);
|
|
55
|
+
static void SetSource(const Napi::CallbackInfo& info);
|
|
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
|
+
enum Orientation {
|
|
82
|
+
NORMAL,
|
|
83
|
+
MIRROR_HORIZ,
|
|
84
|
+
MIRROR_VERT,
|
|
85
|
+
ROTATE_180,
|
|
86
|
+
ROTATE_90_CW,
|
|
87
|
+
ROTATE_270_CW,
|
|
88
|
+
MIRROR_HORIZ_AND_ROTATE_90_CW,
|
|
89
|
+
MIRROR_HORIZ_AND_ROTATE_270_CW
|
|
90
|
+
};
|
|
91
|
+
cairo_status_t loadJPEGFromBuffer(uint8_t *buf, unsigned len);
|
|
92
|
+
cairo_status_t loadJPEG(FILE *stream);
|
|
93
|
+
void jpegToARGB(jpeg_decompress_struct* args, uint8_t* data, uint8_t* src, JPEGDecodeL decode);
|
|
94
|
+
cairo_status_t decodeJPEGIntoSurface(jpeg_decompress_struct *info, Orientation orientation);
|
|
95
|
+
cairo_status_t decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len);
|
|
96
|
+
cairo_status_t assignDataAsMime(uint8_t *data, int len, const char *mime_type);
|
|
97
|
+
|
|
98
|
+
class Reader {
|
|
99
|
+
public:
|
|
100
|
+
virtual bool hasBytes(unsigned n) const = 0;
|
|
101
|
+
virtual uint8_t getNext() = 0;
|
|
102
|
+
virtual void skipBytes(unsigned n) = 0;
|
|
103
|
+
};
|
|
104
|
+
Orientation getExifOrientation(Reader& jpeg);
|
|
105
|
+
void updateDimensionsForOrientation(Orientation orientation);
|
|
106
|
+
void rotatePixels(uint8_t* pixels, int width, int height, int channels, Orientation orientation);
|
|
107
|
+
#endif
|
|
108
|
+
cairo_status_t loadBMPFromBuffer(uint8_t *buf, unsigned len);
|
|
109
|
+
cairo_status_t loadBMP(FILE *stream);
|
|
110
|
+
CanvasError errorInfo;
|
|
111
|
+
void loaded();
|
|
112
|
+
cairo_status_t load();
|
|
113
|
+
~Image();
|
|
114
|
+
|
|
115
|
+
enum {
|
|
116
|
+
DEFAULT
|
|
117
|
+
, LOADING
|
|
118
|
+
, COMPLETE
|
|
119
|
+
} state;
|
|
120
|
+
|
|
121
|
+
enum data_mode_t {
|
|
122
|
+
DATA_IMAGE = 1
|
|
123
|
+
, DATA_MIME = 2
|
|
124
|
+
} data_mode;
|
|
125
|
+
|
|
126
|
+
typedef enum {
|
|
127
|
+
UNKNOWN
|
|
128
|
+
, GIF
|
|
129
|
+
, JPEG
|
|
130
|
+
, PNG
|
|
131
|
+
, SVG
|
|
132
|
+
} type;
|
|
133
|
+
|
|
134
|
+
static type extension(const char *filename);
|
|
135
|
+
|
|
136
|
+
private:
|
|
137
|
+
cairo_surface_t *_surface;
|
|
138
|
+
uint8_t *_data = nullptr;
|
|
139
|
+
int _data_len;
|
|
140
|
+
#ifdef HAVE_RSVG
|
|
141
|
+
RsvgHandle *_rsvg;
|
|
142
|
+
bool _is_svg;
|
|
143
|
+
int _svg_last_width;
|
|
144
|
+
int _svg_last_height;
|
|
145
|
+
#endif
|
|
146
|
+
};
|
package/src/ImageData.cc
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
|
|
2
|
+
|
|
3
|
+
#include "ImageData.h"
|
|
4
|
+
#include "InstanceData.h"
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* Initialize ImageData.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
void
|
|
11
|
+
ImageData::Initialize(Napi::Env& env, Napi::Object& exports) {
|
|
12
|
+
Napi::HandleScope scope(env);
|
|
13
|
+
|
|
14
|
+
InstanceData *data = env.GetInstanceData<InstanceData>();
|
|
15
|
+
|
|
16
|
+
Napi::Function ctor = DefineClass(env, "ImageData", {
|
|
17
|
+
InstanceAccessor<&ImageData::GetWidth>("width", napi_default_jsproperty),
|
|
18
|
+
InstanceAccessor<&ImageData::GetHeight>("height", napi_default_jsproperty)
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
exports.Set("ImageData", ctor);
|
|
22
|
+
data->ImageDataCtor = Napi::Persistent(ctor);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
* Initialize a new ImageData object.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
ImageData::ImageData(const Napi::CallbackInfo& info) : Napi::ObjectWrap<ImageData>(info), env(info.Env()) {
|
|
30
|
+
Napi::TypedArray dataArray;
|
|
31
|
+
uint32_t width;
|
|
32
|
+
uint32_t height;
|
|
33
|
+
uint32_t length;
|
|
34
|
+
|
|
35
|
+
if (info[0].IsNumber() && info[1].IsNumber()) {
|
|
36
|
+
width = info[0].As<Napi::Number>().Uint32Value();
|
|
37
|
+
if (width == 0) {
|
|
38
|
+
Napi::RangeError::New(env, "The source width is zero.").ThrowAsJavaScriptException();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
height = info[1].As<Napi::Number>().Uint32Value();
|
|
42
|
+
if (height == 0) {
|
|
43
|
+
Napi::RangeError::New(env, "The source height is zero.").ThrowAsJavaScriptException();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if ((uint64_t)width * height > INT32_MAX / 4) {
|
|
47
|
+
// INT32_MAX is what Firefox limits ImageData to
|
|
48
|
+
std::string msg = "buffer exceeds " + std::to_string(INT32_MAX) + " bytes";
|
|
49
|
+
Napi::Error::New(env, msg).ThrowAsJavaScriptException();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
length = width * height * 4; // ImageData(w, h) constructor assumes 4 BPP; documented.
|
|
53
|
+
|
|
54
|
+
dataArray = Napi::Uint8Array::New(env, length, napi_uint8_clamped_array);
|
|
55
|
+
} else if (
|
|
56
|
+
info[0].IsTypedArray() &&
|
|
57
|
+
info[0].As<Napi::TypedArray>().TypedArrayType() == napi_uint8_clamped_array &&
|
|
58
|
+
info[1].IsNumber()
|
|
59
|
+
) {
|
|
60
|
+
dataArray = info[0].As<Napi::Uint8Array>();
|
|
61
|
+
|
|
62
|
+
length = dataArray.ElementLength();
|
|
63
|
+
if (length == 0) {
|
|
64
|
+
Napi::RangeError::New(env, "The input data has a zero byte length.").ThrowAsJavaScriptException();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Don't assert that the ImageData length is a multiple of four because some
|
|
69
|
+
// data formats are not 4 BPP.
|
|
70
|
+
|
|
71
|
+
width = info[1].As<Napi::Number>().Uint32Value();
|
|
72
|
+
if (width == 0) {
|
|
73
|
+
Napi::RangeError::New(env, "The source width is zero.").ThrowAsJavaScriptException();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Don't assert that the byte length is a multiple of 4 * width, ditto.
|
|
78
|
+
|
|
79
|
+
if (info[2].IsNumber()) { // Explicit height given
|
|
80
|
+
height = info[2].As<Napi::Number>().Uint32Value();
|
|
81
|
+
} else { // Calculate height assuming 4 BPP
|
|
82
|
+
int size = length / 4;
|
|
83
|
+
height = size / width;
|
|
84
|
+
}
|
|
85
|
+
} else if (
|
|
86
|
+
info[0].IsTypedArray() &&
|
|
87
|
+
info[0].As<Napi::TypedArray>().TypedArrayType() == napi_uint16_array &&
|
|
88
|
+
info[1].IsNumber()
|
|
89
|
+
) { // Intended for RGB16_565 format
|
|
90
|
+
dataArray = info[0].As<Napi::TypedArray>();
|
|
91
|
+
|
|
92
|
+
length = dataArray.ElementLength();
|
|
93
|
+
if (length == 0) {
|
|
94
|
+
Napi::RangeError::New(env, "The input data has a zero byte length.").ThrowAsJavaScriptException();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
width = info[1].As<Napi::Number>().Uint32Value();
|
|
99
|
+
if (width == 0) {
|
|
100
|
+
Napi::RangeError::New(env, "The source width is zero.").ThrowAsJavaScriptException();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (info[2].IsNumber()) { // Explicit height given
|
|
105
|
+
height = info[2].As<Napi::Number>().Uint32Value();
|
|
106
|
+
} else { // Calculate height assuming 2 BPP
|
|
107
|
+
int size = length / 2;
|
|
108
|
+
height = size / width;
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
Napi::TypeError::New(env, "Expected (Uint8ClampedArray, width[, height]), (Uint16Array, width[, height]) or (width, height)").ThrowAsJavaScriptException();
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
_width = width;
|
|
116
|
+
_height = height;
|
|
117
|
+
_data = dataArray.As<Napi::Uint8Array>().Data();
|
|
118
|
+
|
|
119
|
+
info.This().As<Napi::Object>().Set("data", dataArray);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
* Get width.
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
Napi::Value
|
|
127
|
+
ImageData::GetWidth(const Napi::CallbackInfo& info) {
|
|
128
|
+
return Napi::Number::New(env, width());
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/*
|
|
132
|
+
* Get height.
|
|
133
|
+
*/
|
|
134
|
+
|
|
135
|
+
Napi::Value
|
|
136
|
+
ImageData::GetHeight(const Napi::CallbackInfo& info) {
|
|
137
|
+
return Napi::Number::New(env, height());
|
|
138
|
+
}
|
package/src/ImageData.h
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
|
|
2
|
+
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <napi.h>
|
|
6
|
+
#include <stdint.h> // node < 7 uses libstdc++ on macOS which lacks complete c++11
|
|
7
|
+
|
|
8
|
+
class ImageData : public Napi::ObjectWrap<ImageData> {
|
|
9
|
+
public:
|
|
10
|
+
static void Initialize(Napi::Env& env, Napi::Object& exports);
|
|
11
|
+
ImageData(const Napi::CallbackInfo& info);
|
|
12
|
+
Napi::Value GetWidth(const Napi::CallbackInfo& info);
|
|
13
|
+
Napi::Value GetHeight(const Napi::CallbackInfo& info);
|
|
14
|
+
|
|
15
|
+
inline uint32_t width() { return _width; }
|
|
16
|
+
inline uint32_t height() { return _height; }
|
|
17
|
+
inline uint8_t *data() { return _data; }
|
|
18
|
+
|
|
19
|
+
Napi::Env env;
|
|
20
|
+
|
|
21
|
+
private:
|
|
22
|
+
uint32_t _width;
|
|
23
|
+
uint32_t _height;
|
|
24
|
+
uint8_t *_data;
|
|
25
|
+
|
|
26
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#include <napi.h>
|
|
2
|
+
|
|
3
|
+
struct InstanceData {
|
|
4
|
+
Napi::FunctionReference CanvasCtor;
|
|
5
|
+
Napi::FunctionReference CanvasGradientCtor;
|
|
6
|
+
Napi::FunctionReference DOMMatrixCtor;
|
|
7
|
+
Napi::FunctionReference ImageCtor;
|
|
8
|
+
Napi::FunctionReference parseFont;
|
|
9
|
+
Napi::FunctionReference Context2dCtor;
|
|
10
|
+
Napi::FunctionReference ImageDataCtor;
|
|
11
|
+
Napi::FunctionReference CanvasPatternCtor;
|
|
12
|
+
};
|
package/src/JPEGStream.h
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
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
|
+
closure_destination_mgr *dest = (closure_destination_mgr *) cinfo->dest;
|
|
27
|
+
Napi::Env env = dest->closure->canvas->Env();
|
|
28
|
+
Napi::HandleScope scope(env);
|
|
29
|
+
Napi::AsyncContext async(env, "canvas:empty_closure_output_buffer");
|
|
30
|
+
|
|
31
|
+
Napi::Object buf = Napi::Buffer<char>::New(env, (char *)dest->buffer, dest->bufsize);
|
|
32
|
+
|
|
33
|
+
// emit "data"
|
|
34
|
+
dest->closure->cb.MakeCallback(env.Global(), {env.Null(), buf}, async);
|
|
35
|
+
|
|
36
|
+
dest->buffer = (JOCTET *)malloc(dest->bufsize);
|
|
37
|
+
cinfo->dest->next_output_byte = dest->buffer;
|
|
38
|
+
cinfo->dest->free_in_buffer = dest->bufsize;
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
void
|
|
43
|
+
term_closure_destination(j_compress_ptr cinfo){
|
|
44
|
+
closure_destination_mgr *dest = (closure_destination_mgr *) cinfo->dest;
|
|
45
|
+
Napi::Env env = dest->closure->canvas->Env();
|
|
46
|
+
Napi::HandleScope scope(env);
|
|
47
|
+
Napi::AsyncContext async(env, "canvas:term_closure_destination");
|
|
48
|
+
|
|
49
|
+
/* emit remaining data */
|
|
50
|
+
Napi::Object buf = Napi::Buffer<char>::New(env, (char *)dest->buffer, dest->bufsize - dest->pub.free_in_buffer);
|
|
51
|
+
|
|
52
|
+
dest->closure->cb.MakeCallback(env.Global(), {env.Null(), buf}, async);
|
|
53
|
+
|
|
54
|
+
// emit "end"
|
|
55
|
+
dest->closure->cb.MakeCallback(env.Global(), {env.Null(), env.Null()}, async);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
void
|
|
59
|
+
jpeg_closure_dest(j_compress_ptr cinfo, JpegClosure* closure, int bufsize){
|
|
60
|
+
closure_destination_mgr * dest;
|
|
61
|
+
|
|
62
|
+
/* The destination object is made permanent so that multiple JPEG images
|
|
63
|
+
* can be written to the same buffer without re-executing jpeg_mem_dest.
|
|
64
|
+
*/
|
|
65
|
+
if (cinfo->dest == NULL) { /* first time for this JPEG object? */
|
|
66
|
+
cinfo->dest = (struct jpeg_destination_mgr *)
|
|
67
|
+
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
|
|
68
|
+
sizeof(closure_destination_mgr));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
dest = (closure_destination_mgr *) cinfo->dest;
|
|
72
|
+
|
|
73
|
+
cinfo->dest->init_destination = &init_closure_destination;
|
|
74
|
+
cinfo->dest->empty_output_buffer = &empty_closure_output_buffer;
|
|
75
|
+
cinfo->dest->term_destination = &term_closure_destination;
|
|
76
|
+
|
|
77
|
+
dest->closure = closure;
|
|
78
|
+
dest->bufsize = bufsize;
|
|
79
|
+
dest->buffer = (JOCTET *)malloc(bufsize);
|
|
80
|
+
|
|
81
|
+
cinfo->dest->next_output_byte = dest->buffer;
|
|
82
|
+
cinfo->dest->free_in_buffer = dest->bufsize;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void encode_jpeg(jpeg_compress_struct cinfo, cairo_surface_t *surface, int quality, bool progressive, int chromaHSampFactor, int chromaVSampFactor) {
|
|
86
|
+
int w = cairo_image_surface_get_width(surface);
|
|
87
|
+
int h = cairo_image_surface_get_height(surface);
|
|
88
|
+
|
|
89
|
+
cinfo.in_color_space = JCS_RGB;
|
|
90
|
+
cinfo.input_components = 3;
|
|
91
|
+
cinfo.image_width = w;
|
|
92
|
+
cinfo.image_height = h;
|
|
93
|
+
jpeg_set_defaults(&cinfo);
|
|
94
|
+
if (progressive)
|
|
95
|
+
jpeg_simple_progression(&cinfo);
|
|
96
|
+
jpeg_set_quality(&cinfo, quality, (quality < 25) ? 0 : 1);
|
|
97
|
+
cinfo.comp_info[0].h_samp_factor = chromaHSampFactor;
|
|
98
|
+
cinfo.comp_info[0].v_samp_factor = chromaVSampFactor;
|
|
99
|
+
|
|
100
|
+
JSAMPROW slr;
|
|
101
|
+
jpeg_start_compress(&cinfo, TRUE);
|
|
102
|
+
unsigned char *dst;
|
|
103
|
+
unsigned int *src = (unsigned int *)cairo_image_surface_get_data(surface);
|
|
104
|
+
int sl = 0;
|
|
105
|
+
dst = (unsigned char *)malloc(w * 3);
|
|
106
|
+
while (sl < h) {
|
|
107
|
+
unsigned char *dp = dst;
|
|
108
|
+
int x = 0;
|
|
109
|
+
while (x < w) {
|
|
110
|
+
dp[0] = (*src >> 16) & 255;
|
|
111
|
+
dp[1] = (*src >> 8) & 255;
|
|
112
|
+
dp[2] = *src & 255;
|
|
113
|
+
src++;
|
|
114
|
+
dp += 3;
|
|
115
|
+
x++;
|
|
116
|
+
}
|
|
117
|
+
slr = dst;
|
|
118
|
+
jpeg_write_scanlines(&cinfo, &slr, 1);
|
|
119
|
+
sl++;
|
|
120
|
+
}
|
|
121
|
+
free(dst);
|
|
122
|
+
jpeg_finish_compress(&cinfo);
|
|
123
|
+
jpeg_destroy_compress(&cinfo);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
void
|
|
127
|
+
write_to_jpeg_stream(cairo_surface_t *surface, int bufsize, JpegClosure* closure) {
|
|
128
|
+
jpeg_compress_struct cinfo;
|
|
129
|
+
jpeg_error_mgr jerr;
|
|
130
|
+
cinfo.err = jpeg_std_error(&jerr);
|
|
131
|
+
jpeg_create_compress(&cinfo);
|
|
132
|
+
jpeg_closure_dest(&cinfo, closure, bufsize);
|
|
133
|
+
encode_jpeg(
|
|
134
|
+
cinfo,
|
|
135
|
+
surface,
|
|
136
|
+
closure->quality,
|
|
137
|
+
closure->progressive,
|
|
138
|
+
closure->chromaSubsampling,
|
|
139
|
+
closure->chromaSubsampling);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
void
|
|
143
|
+
write_to_jpeg_buffer(cairo_surface_t* surface, JpegClosure* closure) {
|
|
144
|
+
jpeg_compress_struct cinfo;
|
|
145
|
+
jpeg_error_mgr jerr;
|
|
146
|
+
cinfo.err = jpeg_std_error(&jerr);
|
|
147
|
+
jpeg_create_compress(&cinfo);
|
|
148
|
+
cinfo.client_data = closure;
|
|
149
|
+
cinfo.dest = closure->jpeg_dest_mgr;
|
|
150
|
+
encode_jpeg(
|
|
151
|
+
cinfo,
|
|
152
|
+
surface,
|
|
153
|
+
closure->quality,
|
|
154
|
+
closure->progressive,
|
|
155
|
+
closure->chromaSubsampling,
|
|
156
|
+
closure->chromaSubsampling);
|
|
157
|
+
}
|