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 : #include <sal/config.h>
21 :
22 : #include "jpeg.h"
23 : #include <jpeglib.h>
24 : #include <jerror.h>
25 :
26 : #include "JpegWriter.hxx"
27 : #include <vcl/bmpacc.hxx>
28 : #include <vcl/FilterConfigItem.hxx>
29 : #include <vcl/graphicfilter.hxx>
30 :
31 : #define BUFFER_SIZE 4096
32 :
33 : struct DestinationManagerStruct
34 : {
35 : jpeg_destination_mgr pub; /* public fields */
36 : SvStream* stream; /* target stream */
37 : JOCTET * buffer; /* start of buffer */
38 : };
39 :
40 1 : extern "C" void init_destination (j_compress_ptr cinfo)
41 : {
42 1 : DestinationManagerStruct * destination = reinterpret_cast<DestinationManagerStruct *>(cinfo->dest);
43 :
44 : /* Allocate the output buffer -- it will be released when done with image */
45 : destination->buffer = static_cast<JOCTET *>(
46 1 : (*cinfo->mem->alloc_small) (reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE, BUFFER_SIZE * sizeof(JOCTET)));
47 :
48 1 : destination->pub.next_output_byte = destination->buffer;
49 1 : destination->pub.free_in_buffer = BUFFER_SIZE;
50 1 : }
51 :
52 0 : extern "C" boolean empty_output_buffer (j_compress_ptr cinfo)
53 : {
54 0 : DestinationManagerStruct * destination = reinterpret_cast<DestinationManagerStruct *>(cinfo->dest);
55 :
56 0 : if (destination->stream->Write(destination->buffer, BUFFER_SIZE) != (size_t) BUFFER_SIZE)
57 : {
58 0 : ERREXIT(cinfo, JERR_FILE_WRITE);
59 : }
60 :
61 0 : destination->pub.next_output_byte = destination->buffer;
62 0 : destination->pub.free_in_buffer = BUFFER_SIZE;
63 :
64 0 : return TRUE;
65 : }
66 :
67 1 : extern "C" void term_destination (j_compress_ptr cinfo)
68 : {
69 1 : DestinationManagerStruct * destination = reinterpret_cast<DestinationManagerStruct *>(cinfo->dest);
70 1 : size_t datacount = BUFFER_SIZE - destination->pub.free_in_buffer;
71 :
72 : /* Write any data remaining in the buffer */
73 1 : if (datacount > 0)
74 : {
75 1 : if (destination->stream->Write(destination->buffer, datacount) != datacount)
76 : {
77 0 : ERREXIT(cinfo, JERR_FILE_WRITE);
78 : }
79 : }
80 1 : }
81 :
82 1 : void jpeg_svstream_dest (j_compress_ptr cinfo, void* output)
83 : {
84 1 : SvStream* stream = static_cast<SvStream*>(output);
85 : DestinationManagerStruct * destination;
86 :
87 : /* The destination object is made permanent so that multiple JPEG images
88 : * can be written to the same file without re-executing jpeg_svstream_dest.
89 : * This makes it dangerous to use this manager and a different destination
90 : * manager serially with the same JPEG object, because their private object
91 : * sizes may be different. Caveat programmer.
92 : */
93 1 : if (cinfo->dest == NULL)
94 : { /* first time for this JPEG object? */
95 : cinfo->dest = static_cast<jpeg_destination_mgr*>(
96 1 : (*cinfo->mem->alloc_small) (reinterpret_cast<j_common_ptr>(cinfo), JPOOL_PERMANENT, sizeof(DestinationManagerStruct)));
97 : }
98 :
99 1 : destination = reinterpret_cast<DestinationManagerStruct *>(cinfo->dest);
100 1 : destination->pub.init_destination = init_destination;
101 1 : destination->pub.empty_output_buffer = empty_output_buffer;
102 1 : destination->pub.term_destination = term_destination;
103 1 : destination->stream = stream;
104 1 : }
105 :
106 1 : JPEGWriter::JPEGWriter( SvStream& rStream, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData, bool* pExportWasGrey ) :
107 : mrStream ( rStream ),
108 : mpReadAccess ( NULL ),
109 : mpBuffer ( NULL ),
110 : mbNative ( false ),
111 1 : mpExpWasGrey ( pExportWasGrey )
112 : {
113 1 : FilterConfigItem aConfigItem( const_cast<css::uno::Sequence< css::beans::PropertyValue >*>(pFilterData) );
114 1 : mbGreys = aConfigItem.ReadInt32( "ColorMode", 0 ) != 0;
115 1 : mnQuality = aConfigItem.ReadInt32( "Quality", 75 );
116 1 : maChromaSubsampling = aConfigItem.ReadInt32( "ChromaSubsamplingMode", 0 );
117 :
118 1 : if ( pFilterData )
119 : {
120 1 : int nArgs = pFilterData->getLength();
121 1 : const css::beans::PropertyValue* pValues = pFilterData->getConstArray();
122 5 : while( nArgs-- )
123 : {
124 3 : if ( pValues->Name == "StatusIndicator" )
125 : {
126 0 : pValues->Value >>= mxStatusIndicator;
127 : }
128 3 : pValues++;
129 : }
130 1 : }
131 1 : }
132 :
133 100 : void* JPEGWriter::GetScanline( long nY )
134 : {
135 100 : void* pScanline = NULL;
136 :
137 100 : if( mpReadAccess )
138 : {
139 100 : if( mbNative )
140 : {
141 0 : pScanline = mpReadAccess->GetScanline( nY );
142 : }
143 100 : else if( mpBuffer )
144 : {
145 100 : BitmapColor aColor;
146 100 : long nWidth = mpReadAccess->Width();
147 100 : sal_uInt8* pTmp = mpBuffer;
148 :
149 100 : if( mpReadAccess->HasPalette() )
150 : {
151 0 : for( long nX = 0L; nX < nWidth; nX++ )
152 : {
153 0 : aColor = mpReadAccess->GetPaletteColor( mpReadAccess->GetPixelIndex( nY, nX ) );
154 0 : *pTmp++ = aColor.GetRed();
155 0 : if ( !mbGreys )
156 : {
157 0 : *pTmp++ = aColor.GetGreen();
158 0 : *pTmp++ = aColor.GetBlue();
159 : }
160 : }
161 : }
162 : else
163 : {
164 10100 : for( long nX = 0L; nX < nWidth; nX++ )
165 : {
166 10000 : aColor = mpReadAccess->GetPixel( nY, nX );
167 10000 : *pTmp++ = aColor.GetRed();
168 10000 : if ( !mbGreys )
169 : {
170 0 : *pTmp++ = aColor.GetGreen();
171 0 : *pTmp++ = aColor.GetBlue();
172 : }
173 : }
174 : }
175 :
176 100 : pScanline = mpBuffer;
177 : }
178 : }
179 :
180 100 : return pScanline;
181 : }
182 :
183 1 : bool JPEGWriter::Write( const Graphic& rGraphic )
184 : {
185 1 : bool bRet = false;
186 :
187 1 : if ( mxStatusIndicator.is() )
188 : {
189 0 : OUString aMsg;
190 0 : mxStatusIndicator->start( aMsg, 100 );
191 : }
192 :
193 1 : Bitmap aGraphicBmp( rGraphic.GetBitmap() );
194 :
195 1 : if ( mbGreys )
196 : {
197 0 : if ( !aGraphicBmp.Convert( BMP_CONVERSION_8BIT_GREYS ) )
198 0 : aGraphicBmp = rGraphic.GetBitmap();
199 : }
200 :
201 1 : mpReadAccess = aGraphicBmp.AcquireReadAccess();
202 :
203 1 : if ( !mbGreys ) // bitmap was not explicitly converted into greyscale,
204 : { // check if source is greyscale only
205 :
206 1 : bool bIsGrey = true;
207 :
208 1 : long nWidth = mpReadAccess->Width();
209 101 : for ( long nY = 0; bIsGrey && ( nY < mpReadAccess->Height() ); nY++ )
210 : {
211 100 : BitmapColor aColor;
212 10100 : for( long nX = 0L; bIsGrey && ( nX < nWidth ); nX++ )
213 : {
214 20000 : aColor = mpReadAccess->HasPalette() ? mpReadAccess->GetPaletteColor( mpReadAccess->GetPixelIndex( nY, nX ) )
215 10000 : : mpReadAccess->GetPixel( nY, nX );
216 10000 : bIsGrey = ( aColor.GetRed() == aColor.GetGreen() ) && ( aColor.GetRed() == aColor.GetBlue() );
217 : }
218 100 : }
219 1 : if ( bIsGrey )
220 1 : mbGreys = true;
221 : }
222 :
223 1 : if( mpExpWasGrey )
224 1 : *mpExpWasGrey = mbGreys;
225 :
226 1 : if( mpReadAccess )
227 : {
228 1 : mbNative = ( mpReadAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB );
229 :
230 1 : if( !mbNative )
231 1 : mpBuffer = new sal_uInt8[ AlignedWidth4Bytes( mbGreys ? mpReadAccess->Width() * 8L : mpReadAccess->Width() * 24L ) ];
232 :
233 1 : bRet = WriteJPEG( this, &mrStream, mpReadAccess->Width(), mpReadAccess->Height(), mbGreys, mnQuality, maChromaSubsampling, mxStatusIndicator );
234 :
235 1 : delete[] mpBuffer;
236 1 : mpBuffer = NULL;
237 :
238 1 : Bitmap::ReleaseAccess( mpReadAccess );
239 1 : mpReadAccess = NULL;
240 : }
241 1 : if ( mxStatusIndicator.is() )
242 0 : mxStatusIndicator->end();
243 :
244 1 : return bRet;
245 801 : }
246 :
247 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|