UNPKG

4.74 kBtext/x-cView Raw
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
12struct closure_destination_mgr {
13 jpeg_destination_mgr pub;
14 JpegClosure* closure;
15 JOCTET *buffer;
16 int bufsize;
17};
18
19void
20init_closure_destination(j_compress_ptr cinfo){
21 // we really don't have to do anything here
22}
23
24boolean
25empty_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
45void
46term_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
68void
69jpeg_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
95void 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
136void
137write_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
152void
153write_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}