1 | #pragma once
|
2 |
|
3 | #include "closure.h"
|
4 | #include <jpeglib.h>
|
5 | #include <jerror.h>
|
6 |
|
7 |
|
8 |
|
9 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 | |
73 |
|
74 |
|
75 | if (cinfo->dest == NULL) {
|
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 | }
|