Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : extern "C"
21 : {
22 : #include "stdio.h"
23 : #include "jpeg.h"
24 : #include <jpeglib.h>
25 : #include <jerror.h>
26 : }
27 :
28 : #include <tools/solar.h>
29 : #include <vcl/bmpacc.hxx>
30 : #include <vcl/FilterConfigItem.hxx>
31 : #include <vcl/graphicfilter.hxx>
32 : #include "JpegWriter.hxx"
33 :
34 : #define BUFFER_SIZE 4096
35 :
36 100 : extern "C" void* GetScanline( void* pJPEGWriter, long nY )
37 : {
38 100 : return ( (JPEGWriter*) pJPEGWriter )->GetScanline( nY );
39 : }
40 :
41 2 : struct JPEGCallbackStruct
42 : {
43 : css::uno::Reference< css::task::XStatusIndicator > xStatusIndicator;
44 : };
45 :
46 100 : extern "C" long JPEGCallback( void* pCallbackData, long nPercent )
47 : {
48 100 : JPEGCallbackStruct* pCallbackStruct = (JPEGCallbackStruct*)pCallbackData;
49 100 : if ( pCallbackStruct && pCallbackStruct->xStatusIndicator.is() )
50 : {
51 0 : pCallbackStruct->xStatusIndicator->setValue( nPercent );
52 : }
53 100 : return 0L;
54 : }
55 :
56 : typedef struct
57 : {
58 : struct jpeg_destination_mgr pub; /* public fields */
59 : SvStream* stream; /* target stream */
60 : JOCTET * buffer; /* start of buffer */
61 : } DestinationManagerStruct;
62 :
63 : typedef DestinationManagerStruct* DestinationManagerStructPointer;
64 :
65 1 : extern "C" void init_destination (j_compress_ptr cinfo)
66 : {
67 1 : DestinationManagerStructPointer destination = (DestinationManagerStructPointer) cinfo->dest;
68 :
69 : /* Allocate the output buffer -- it will be released when done with image */
70 : destination->buffer = (JOCTET *)
71 1 : (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, BUFFER_SIZE * sizeof(JOCTET));
72 :
73 1 : destination->pub.next_output_byte = destination->buffer;
74 1 : destination->pub.free_in_buffer = BUFFER_SIZE;
75 1 : }
76 :
77 0 : extern "C" boolean empty_output_buffer (j_compress_ptr cinfo)
78 : {
79 0 : DestinationManagerStructPointer destination = (DestinationManagerStructPointer) cinfo->dest;
80 :
81 0 : if (destination->stream->Write(destination->buffer, BUFFER_SIZE) != (size_t) BUFFER_SIZE)
82 : {
83 0 : ERREXIT(cinfo, JERR_FILE_WRITE);
84 : }
85 :
86 0 : destination->pub.next_output_byte = destination->buffer;
87 0 : destination->pub.free_in_buffer = BUFFER_SIZE;
88 :
89 0 : return sal_True;
90 : }
91 :
92 1 : extern "C" void term_destination (j_compress_ptr cinfo)
93 : {
94 1 : DestinationManagerStructPointer destination = (DestinationManagerStructPointer) cinfo->dest;
95 1 : size_t datacount = BUFFER_SIZE - destination->pub.free_in_buffer;
96 :
97 : /* Write any data remaining in the buffer */
98 1 : if (datacount > 0)
99 : {
100 1 : if (destination->stream->Write(destination->buffer, datacount) != datacount)
101 : {
102 0 : ERREXIT(cinfo, JERR_FILE_WRITE);
103 : }
104 : }
105 1 : }
106 :
107 1 : extern "C" void jpeg_svstream_dest (j_compress_ptr cinfo, void* output)
108 : {
109 1 : SvStream* stream = (SvStream*) output;
110 : DestinationManagerStructPointer destination;
111 :
112 : /* The destination object is made permanent so that multiple JPEG images
113 : * can be written to the same file without re-executing jpeg_svstream_dest.
114 : * This makes it dangerous to use this manager and a different destination
115 : * manager serially with the same JPEG object, because their private object
116 : * sizes may be different. Caveat programmer.
117 : */
118 1 : if (cinfo->dest == NULL)
119 : { /* first time for this JPEG object? */
120 : cinfo->dest = (struct jpeg_destination_mgr*)
121 1 : (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(DestinationManagerStruct));
122 : }
123 :
124 1 : destination = (DestinationManagerStructPointer) cinfo->dest;
125 1 : destination->pub.init_destination = init_destination;
126 1 : destination->pub.empty_output_buffer = empty_output_buffer;
127 1 : destination->pub.term_destination = term_destination;
128 1 : destination->stream = stream;
129 1 : }
130 :
131 1 : JPEGWriter::JPEGWriter( SvStream& rStream, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData, bool* pExportWasGrey ) :
132 : mrStream ( rStream ),
133 : mpReadAccess ( NULL ),
134 : mpBuffer ( NULL ),
135 1 : mpExpWasGrey ( pExportWasGrey )
136 : {
137 1 : FilterConfigItem aConfigItem( (css::uno::Sequence< css::beans::PropertyValue >*) pFilterData );
138 1 : mbGreys = aConfigItem.ReadInt32( "ColorMode", 0 ) != 0;
139 1 : mnQuality = aConfigItem.ReadInt32( "Quality", 75 );
140 1 : maChromaSubsampling = aConfigItem.ReadInt32( "ChromaSubsamplingMode", 0 );
141 :
142 1 : if ( pFilterData )
143 : {
144 1 : int nArgs = pFilterData->getLength();
145 1 : const css::beans::PropertyValue* pValues = pFilterData->getConstArray();
146 5 : while( nArgs-- )
147 : {
148 3 : if ( pValues->Name == "StatusIndicator" )
149 : {
150 0 : pValues->Value >>= mxStatusIndicator;
151 : }
152 3 : pValues++;
153 : }
154 1 : }
155 1 : }
156 :
157 100 : void* JPEGWriter::GetScanline( long nY )
158 : {
159 100 : void* pScanline = NULL;
160 :
161 100 : if( mpReadAccess )
162 : {
163 100 : if( mbNative )
164 : {
165 0 : pScanline = mpReadAccess->GetScanline( nY );
166 : }
167 100 : else if( mpBuffer )
168 : {
169 100 : BitmapColor aColor;
170 100 : long nWidth = mpReadAccess->Width();
171 100 : sal_uInt8* pTmp = mpBuffer;
172 :
173 100 : if( mpReadAccess->HasPalette() )
174 : {
175 0 : for( long nX = 0L; nX < nWidth; nX++ )
176 : {
177 0 : aColor = mpReadAccess->GetPaletteColor( mpReadAccess->GetPixelIndex( nY, nX ) );
178 0 : *pTmp++ = aColor.GetRed();
179 0 : if ( !mbGreys )
180 : {
181 0 : *pTmp++ = aColor.GetGreen();
182 0 : *pTmp++ = aColor.GetBlue();
183 : }
184 : }
185 : }
186 : else
187 : {
188 10100 : for( long nX = 0L; nX < nWidth; nX++ )
189 : {
190 10000 : aColor = mpReadAccess->GetPixel( nY, nX );
191 10000 : *pTmp++ = aColor.GetRed();
192 10000 : if ( !mbGreys )
193 : {
194 0 : *pTmp++ = aColor.GetGreen();
195 0 : *pTmp++ = aColor.GetBlue();
196 : }
197 : }
198 : }
199 :
200 100 : pScanline = mpBuffer;
201 : }
202 : }
203 :
204 100 : return pScanline;
205 : }
206 :
207 1 : sal_Bool JPEGWriter::Write( const Graphic& rGraphic )
208 : {
209 1 : sal_Bool bRet = sal_False;
210 :
211 1 : if ( mxStatusIndicator.is() )
212 : {
213 0 : OUString aMsg;
214 0 : mxStatusIndicator->start( aMsg, 100 );
215 : }
216 :
217 1 : Bitmap aGraphicBmp( rGraphic.GetBitmap() );
218 :
219 1 : if ( mbGreys )
220 : {
221 0 : if ( !aGraphicBmp.Convert( BMP_CONVERSION_8BIT_GREYS ) )
222 0 : aGraphicBmp = rGraphic.GetBitmap();
223 : }
224 :
225 1 : mpReadAccess = aGraphicBmp.AcquireReadAccess();
226 :
227 1 : if ( !mbGreys ) // bitmap was not explicitly converted into greyscale,
228 : { // check if source is greyscale only
229 :
230 1 : bool bIsGrey = true;
231 :
232 1 : long nWidth = mpReadAccess->Width();
233 101 : for ( long nY = 0; bIsGrey && ( nY < mpReadAccess->Height() ); nY++ )
234 : {
235 100 : BitmapColor aColor;
236 10100 : for( long nX = 0L; bIsGrey && ( nX < nWidth ); nX++ )
237 : {
238 20000 : aColor = mpReadAccess->HasPalette() ? mpReadAccess->GetPaletteColor( mpReadAccess->GetPixelIndex( nY, nX ) )
239 10000 : : mpReadAccess->GetPixel( nY, nX );
240 10000 : bIsGrey = ( aColor.GetRed() == aColor.GetGreen() ) && ( aColor.GetRed() == aColor.GetBlue() );
241 : }
242 100 : }
243 1 : if ( bIsGrey )
244 1 : mbGreys = sal_True;
245 : }
246 :
247 1 : if( mpExpWasGrey )
248 1 : *mpExpWasGrey = mbGreys;
249 :
250 1 : if( mpReadAccess )
251 : {
252 1 : mbNative = ( mpReadAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB );
253 :
254 1 : if( !mbNative )
255 1 : mpBuffer = new sal_uInt8[ AlignedWidth4Bytes( mbGreys ? mpReadAccess->Width() * 8L : mpReadAccess->Width() * 24L ) ];
256 :
257 1 : JPEGCallbackStruct aCallbackData;
258 1 : aCallbackData.xStatusIndicator = mxStatusIndicator;
259 1 : bRet = (sal_Bool) WriteJPEG( this, &mrStream, mpReadAccess->Width(), mpReadAccess->Height(), mbGreys, mnQuality, maChromaSubsampling, &aCallbackData );
260 :
261 1 : delete[] mpBuffer;
262 1 : mpBuffer = NULL;
263 :
264 1 : aGraphicBmp.ReleaseAccess( mpReadAccess );
265 1 : mpReadAccess = NULL;
266 : }
267 1 : if ( mxStatusIndicator.is() )
268 0 : mxStatusIndicator->end();
269 :
270 1 : return bRet;
271 465 : }
272 :
273 :
274 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|