source: trunk/Lucide/gui/lucidethumbs.cpp @ 458

Last change on this file since 458 was 458, checked in by Silvan Scherrer, 11 years ago

possible fixed ticket:135

File size: 16.3 KB
Line 
1/* ***** BEGIN LICENSE BLOCK *****
2 * Version: CDDL 1.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the COMMON DEVELOPMENT AND
5 * DISTRIBUTION LICENSE (CDDL) Version 1.0 (the "License"); you may not use
6 * this file except in compliance with the License. You may obtain a copy of
7 * the License at http://www.sun.com/cddl/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Initial Developer of the Original Code is
15 * Eugene Romanenko, netlabs.org.
16 * Portions created by the Initial Developer are Copyright (C) 2006
17 * the Initial Developer. All Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * Alternatively, the contents of this file may be used under the terms of
22 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
23 * in which case the provisions of the LGPL are applicable instead of those
24 * above. If you wish to allow use of your version of this file only under the
25 * terms of the LGPL, and not to allow others to use your version of this file
26 * under the terms of the CDDL, indicate your decision by deleting the
27 * provisions above and replace them with the notice and other provisions
28 * required by the LGPL. If you do not delete the provisions above, a recipient
29 * may use your version of this file under the terms of any one of the CDDL
30 * or the LGPL.
31 *
32 * ***** END LICENSE BLOCK ***** */
33
34
35#include "os2all.h"
36#include <mmioos2.h>
37
38#include <stdlib.h>
39#include <string.h>
40#include <io.h>
41#include <fcntl.h>
42#include <algorithm>
43
44#include <ludoc.xh>
45
46#include "Lucide.h"
47#include "luutils.h"
48
49#define LUTHUMB_SIZE_X  256
50#define LUTHUMB_SIZE_Y  256
51static const char * const LUTHUMB_EA_NAME = "LUCIDE_THUMBNAIL";
52
53
54static bool loadMMIOFuncs();
55
56static HMODULE mmioHndl = NULLHANDLE;
57static bool mmioFuncsLoaded = loadMMIOFuncs();
58
59static void freeMmio()
60{
61    if ( mmioHndl != NULLHANDLE ) {
62        DosFreeModule( mmioHndl );
63    }
64}
65
66HMMIO  APIENTRY (*pMmioOpen)(PSZ,PMMIOINFO,ULONG);
67LONG   APIENTRY (*pMmioWrite)(HMMIO,PCHAR,LONG);
68ULONG  APIENTRY (*pMmioSetHeader)(HMMIO,PVOID,LONG,PLONG,ULONG,ULONG);
69USHORT APIENTRY (*pMmioClose)(HMMIO,USHORT);
70ULONG  APIENTRY (*pMmioIdentifyFile)(PSZ,PMMIOINFO,PMMFORMATINFO,PFOURCC,ULONG,ULONG);
71ULONG  APIENTRY (*pMmioQueryHeaderLength)(HMMIO,PLONG,ULONG,ULONG);
72ULONG  APIENTRY (*pMmioGetHeader)(HMMIO,PVOID,LONG,PLONG,ULONG,ULONG);
73LONG   APIENTRY (*pMmioRead)(HMMIO,PCHAR,LONG);
74
75static bool loadMMIOFuncs()
76{
77    bool res = false;
78    do
79    {
80        if ( DosLoadModule( NULL, 0, "MMIO", &mmioHndl ) != 0 )
81            break;
82        if ( DosQueryProcAddr( mmioHndl, 0, "mmioOpen", (PFN *)&pMmioOpen ) != 0 )
83            break;
84        if ( DosQueryProcAddr( mmioHndl, 0, "mmioWrite", (PFN *)&pMmioWrite ) != 0 )
85            break;
86        if ( DosQueryProcAddr( mmioHndl, 0, "mmioSetHeader", (PFN *)&pMmioSetHeader ) != 0 )
87            break;
88        if ( DosQueryProcAddr( mmioHndl, 0, "mmioClose", (PFN *)&pMmioClose ) != 0 )
89            break;
90        if ( DosQueryProcAddr( mmioHndl, 0, "mmioIdentifyFile", (PFN *)&pMmioIdentifyFile ) != 0 )
91            break;
92        if ( DosQueryProcAddr( mmioHndl, 0, "mmioQueryHeaderLength", (PFN *)&pMmioQueryHeaderLength ) != 0 )
93            break;
94        if ( DosQueryProcAddr( mmioHndl, 0, "mmioGetHeader", (PFN *)&pMmioGetHeader ) != 0 )
95            break;
96        if ( DosQueryProcAddr( mmioHndl, 0, "mmioRead", (PFN *)&pMmioRead ) != 0 )
97            break;
98
99        res = true;
100    } while (0);
101
102    atexit( freeMmio );
103
104    return res;
105}
106
107
108static bool saveToImage( char *pszFileName, char *format,
109                         ULONG width, ULONG height, ULONG row_size,
110                         ULONG bpp, char* src_buf )
111{
112    bool              ret = false;
113    MMIOINFO          mmioinfoTarget;
114    HMMIO             hmmioTarget;
115    PBYTE             pbBuffer = NULL;
116    ULONG             cbBuffer,cbBitmapInfo, rl;
117    ULONG             rc;
118    MMIMAGEHEADER     mmImgHdr;
119    BITMAPINFO2       bmp;
120    FOURCC            saveas;
121
122    rl = ( ( bpp * width + 31 ) / 32 ) * 4;
123    cbBuffer = rl * height;
124
125    if ( !format ) {
126        return ret;
127    }
128
129    saveas = mmioFOURCC( format[0], format[1], format[2], format[3] );
130
131    // fill bmp
132    memset( &bmp, 0, sizeof( bmp ) );
133    bmp.cbFix         = sizeof( bmp );
134    bmp.cx            = width;
135    bmp.cy            = height;
136    bmp.cPlanes       = 1;
137    bmp.cBitCount     = bpp;
138    bmp.ulCompression = 0;
139    bmp.cbImage       = cbBuffer;
140
141    cbBitmapInfo = sizeof( bmp );
142
143    memset( &mmioinfoTarget, 0L, sizeof( MMIOINFO ) );
144    mmioinfoTarget.fccIOProc = saveas;
145    mmioinfoTarget.ulTranslate = MMIO_TRANSLATEHEADER | MMIO_TRANSLATEDATA;
146    hmmioTarget = pMmioOpen( pszFileName, &mmioinfoTarget, MMIO_CREATE | MMIO_WRITE |
147                                                        MMIO_DENYWRITE | MMIO_NOIDENTIFY );
148    if ( hmmioTarget )
149    {
150        ULONG ulBytesWritten;
151
152        memset( &mmImgHdr, 0, sizeof( MMIMAGEHEADER ) );
153        mmImgHdr.ulHeaderLength = sizeof(MMIMAGEHEADER);
154        mmImgHdr.ulContentType = MMIO_IMAGE_UNKNOWN;
155        mmImgHdr.ulMediaType = MMIO_MEDIATYPE_IMAGE;
156        mmImgHdr.mmXDIBHeader.XDIBHeaderPrefix.ulMemSize = cbBuffer;
157        mmImgHdr.mmXDIBHeader.XDIBHeaderPrefix.ulPelFormat = ( bpp < 24 ) ?
158                        mmioFOURCC('p','a','l','b') : mmioFOURCC('r','g','b','b');
159
160        mmImgHdr.mmXDIBHeader.XDIBHeaderPrefix.usTransType = 0;
161        mmImgHdr.mmXDIBHeader.XDIBHeaderPrefix.ulTransVal = 0;
162        memcpy( &mmImgHdr.mmXDIBHeader.BMPInfoHeader2, &bmp, cbBitmapInfo );
163        rc = pMmioSetHeader( hmmioTarget, &mmImgHdr, sizeof(MMIMAGEHEADER),
164                                    (PLONG)&ulBytesWritten, 0L, 0L);
165        if ( rc == MMIO_SUCCESS )
166        {
167            for ( int k=0; k < height; k++ )
168            {
169                char *line = src_buf + ( k * row_size );
170
171                ulBytesWritten = pMmioWrite( hmmioTarget, (char*)line, rl );
172
173                if ( ( ulBytesWritten == MMIO_ERROR ) || ( ulBytesWritten == 0 ) ) {
174                    break;
175                }
176            }
177
178            if ( ( ulBytesWritten != MMIO_ERROR ) && ( ulBytesWritten != 0 ) ) {
179                ret = true;
180            }
181        }
182
183        pMmioClose( hmmioTarget, 0L );
184    }
185
186    return ret;
187}
188
189static BOOL set_ea( const char *file_name, const char *ea_name,
190                    const char *ea_data, int ea_data_len )
191{
192    APIRET rc = 0;
193    EAOP2  op;
194
195    char *databuf = new char[(64*2*1024)+1024]; // twice 64K for EA data + 1024 for any case
196
197    op.fpGEA2List = (PGEA2LIST)0;
198    op.fpFEA2List = (PFEA2LIST)databuf;
199
200    int  ea_name_len = strlen( ea_name );
201    if ( ea_name_len > 255 ) {
202        delete databuf;
203        return FALSE;
204    }
205
206    char *databufp = databuf + sizeof( long );
207    *((long*)databufp) = 0; // Next field offset is zero - just one field here
208    databufp += sizeof(long);
209    *databufp++ = 0; // not critical
210    *databufp++ = (char)ea_name_len;
211    *((short*)databufp) = (short)ea_data_len;
212    databufp += sizeof(short);
213    memcpy( databufp, ea_name, ea_name_len+1 ); // with trailing zero
214    databufp += ea_name_len+1;
215
216    // set the ea type and length
217    *((short*)databufp) = EAT_BINARY;
218    databufp += sizeof(short);
219    *((short*)databufp) = (short)ea_data_len;
220    databufp += sizeof(short);
221
222    memcpy( databufp, ea_data, ea_data_len ); // with trailing zero
223    databufp += ea_data_len;
224
225    *((long*)databuf) = databufp-databuf; // Size of all that stuff
226
227    // HPFS386 workaround
228    // Save timestamp (setting EA drops timestamp on HPFS386)
229    APIRET qpirc = 0;
230    FILESTATUS3 fs = { 0 };
231    qpirc = DosQueryPathInfo( file_name, FIL_STANDARD, &fs, sizeof( fs ) );
232
233    // Write EA
234    rc = DosSetPathInfo( file_name, FIL_QUERYEASIZE, &op, sizeof(op), 0);
235    delete databuf;
236
237    // Restore timestamp
238    if ( qpirc == 0 ) {
239        DosSetPathInfo( file_name, FIL_STANDARD, &fs, sizeof( fs ), DSPI_WRTTHRU );
240    }
241
242    if ( rc != 0 ) {
243        return FALSE;
244    }
245
246    return TRUE;
247}
248
249bool Lucide::isThumbNeeded( const char *fn )
250{
251    // First, check if mmio is available
252    if ( !mmioFuncsLoaded ) {
253        return false;
254    }
255
256    // Second, check if file is writeable
257    if ( access( fn, W_OK ) != 0 ) {
258        // Isn't writable, do not waste time to render thumbnail
259        return false;
260    }
261
262    // Third, check if thumbnail EA already present
263    UCHAR    EnumBuf[200] = {0};      // Data Buffer
264    ULONG    ulEnumCnt    = 0;        // Count of entries to return
265    FEA2     *ptr         = NULL;     // Pointer to data items returned
266    ULONG    ulTemp       = 0;
267    APIRET   rc           = 0;
268
269    ulEnumCnt = (ULONG)-1; // Request as many attributes as will fit in buffer
270
271    rc = DosEnumAttribute( ENUMEA_REFTYPE_PATH, (PVOID)fn, 1L, &EnumBuf, sizeof( EnumBuf ),
272                           &ulEnumCnt, ENUMEA_LEVEL_NO_VALUE );
273
274    if ( rc != 0 ) {
275        // error look for EA names, return 'false' to prevent EA creation
276        // as if enum failed, creation may also fail
277        return false;
278    }
279
280    ptr = (FEA2 *)EnumBuf; // Mask the buffer pointer to an FEA2 structure
281
282    for ( ULONG i = 0; i < ulEnumCnt; i++ )
283    {
284        if ( strcmp( ptr->szName, LUTHUMB_EA_NAME ) == 0 ) {
285            return false; // Thumbnail already present
286        }
287        /// increment the ptr with the value in oNextEntryOffset
288        ulTemp = ptr->oNextEntryOffset + (ULONG)ptr;
289        ptr = (FEA2 *)ulTemp;
290    }
291    return true;
292}
293
294
295void Lucide::createThumbnail( LuDocument *doc )
296{
297    if ( !doc->isScalable( ev ) ) {
298        return;
299    }
300
301    // render first page
302    double width = 0, height = 0;
303    doc->getPageSize( ev, 0, &width, &height );
304    double zoom = std::min( (double)LUTHUMB_SIZE_X / width, (double)LUTHUMB_SIZE_Y / height );
305    short bpp = doc->getBpp( ev );
306
307    long rx = width * zoom;
308    long ry = height * zoom;
309    LuPixbuf *pixbuf = new LuPixbuf( ev, rx, ry, bpp );
310    if ( !doc->renderPageToPixbuf( ev, 0, 0, 0, rx, ry, zoom, 0, pixbuf, NULL, NULL ) ) {
311        delete pixbuf;
312        return;
313    }
314
315    char *tmpgif = new char[ CCHMAXPATH ];
316    getTmpDir( tmpgif );
317    strcat( tmpgif, "LUTHUMB.TMP" );
318
319    // Workaround: GIF mmio proc hangs on 32-bit images, convert to 24 bit
320    if ( bpp == 4 )
321    {
322        LuPixbuf *pb = new LuPixbuf( ev, rx, ry, 3 );
323
324        char *src = (char *)pixbuf->getDataPtr( ev );
325        char *dst = (char *)pb->getDataPtr( ev );
326        int src_rowstride = pixbuf->getRowSize( ev );
327        int dst_rowstride = pb->getRowSize( ev );
328
329        int i, j, l, m;
330        for ( i = 0; i < ry; i++ )
331        {
332            char *src_line = src + ( i * src_rowstride );
333            char *dst_line = dst + ( i * dst_rowstride );
334
335            // source 4 Bpp, dest 3 Bpp
336            for ( j = 0, l = 0, m = 0; j < rx; j++ ) {
337                dst_line[ l++ ] = src_line[ m++ ];
338                dst_line[ l++ ] = src_line[ m++ ];
339                dst_line[ l++ ] = src_line[ m++ ];
340                m++;
341            }
342        }
343
344        delete pixbuf;
345        pixbuf = pb;
346        bpp = 3;
347    }
348
349    bool saved = saveToImage( tmpgif, "GIFC", rx, ry, pixbuf->getRowSize( ev ),
350                              bpp * 8, (char *)pixbuf->getDataPtr( ev ) );
351    delete pixbuf;
352    if ( saved )
353    {
354        // If image file saved, read file content into
355        // thumbnailData buffer, to be able write it into EA
356        // when document will be closed
357        int h = open( tmpgif, O_RDONLY | O_BINARY | O_NOINHERIT );
358        if ( h != -1 )
359        {
360            long flen = filelength( h );
361            if ( flen > 0 )
362            {
363                thumbnailData = new char[ flen ];
364                thumbnailDataLen = flen;
365                if ( read( h, thumbnailData, flen ) != flen ) {
366                    delete thumbnailData;
367                    thumbnailData = NULL;
368                    thumbnailDataLen = 0;
369                }
370            }
371            close( h );
372        }
373    }
374    // if tmp file exist - delete it
375    if ( access( tmpgif, F_OK ) == 0 ) {
376        unlink( tmpgif );
377    }
378    delete tmpgif;
379}
380
381void Lucide::writeThumbnail( const char *fn )
382{
383    if ( ( thumbnailData == NULL ) || ( thumbnailDataLen == 0 ) ) {
384        return;
385    }
386
387    set_ea( fn, LUTHUMB_EA_NAME, (const char *)thumbnailData, thumbnailDataLen );
388    delete thumbnailData;
389    thumbnailData = NULL;
390    thumbnailDataLen = 0;
391}
392
393
394HBITMAP LoadBitmap( HAB hab, HDC hdc, HPS *hps, PSZ pszFileName )
395{
396    HBITMAP       hbm;
397    MMIOINFO      mmioinfo;
398    MMFORMATINFO  mmFormatInfo;
399    HMMIO         hmmio;
400    ULONG         ulImageHeaderLength;
401    MMIMAGEHEADER mmImgHdr;
402    ULONG         ulBytesRead;
403    ULONG         dwNumRowBytes;
404    PBYTE         pRowBuffer;
405    ULONG         dwRowCount;
406    SIZEL         ImageSize;
407    ULONG         dwHeight, dwWidth;
408    SHORT         wBitCount;
409    FOURCC        fccStorageSystem;
410    ULONG         dwPadBytes;
411    ULONG         dwRowBits;
412    ULONG         ulReturnCode;
413    ULONG         dwReturnCode;
414    HBITMAP       hbReturnCode;
415    LONG          lReturnCode;
416    FOURCC        fccIOProc;
417
418
419    ulReturnCode = pMmioIdentifyFile( pszFileName, 0L, &mmFormatInfo,
420                                      &fccStorageSystem, 0L, 0L );
421    if ( ulReturnCode == MMIO_ERROR ) {
422         return NULLHANDLE;
423    }
424
425    if( mmFormatInfo.fccIOProc == FOURCC_DOS ) {
426         return NULLHANDLE;
427    }
428
429    if ( (mmFormatInfo.ulMediaType != MMIO_MEDIATYPE_IMAGE) ||
430         ((mmFormatInfo.ulFlags & MMIO_CANREADTRANSLATED) == 0) ) {
431         return NULLHANDLE;
432    }
433    else {
434         fccIOProc = mmFormatInfo.fccIOProc;
435    }
436
437    memset( &mmioinfo, 0L, sizeof( MMIOINFO ) );
438    mmioinfo.fccIOProc = fccIOProc;
439    mmioinfo.ulTranslate = MMIO_TRANSLATEHEADER | MMIO_TRANSLATEDATA;
440
441    hmmio = pMmioOpen( (PSZ)pszFileName, &mmioinfo,
442                      MMIO_READ | MMIO_DENYWRITE | MMIO_NOIDENTIFY );
443
444    if ( !hmmio ) {
445         return NULLHANDLE;
446    }
447
448    dwReturnCode = pMmioQueryHeaderLength( hmmio, (PLONG)&ulImageHeaderLength, 0L, 0L );
449
450    if ( ulImageHeaderLength != sizeof ( MMIMAGEHEADER ) ) {
451         pMmioClose( hmmio, 0L );
452         return NULLHANDLE;
453    }
454
455    ulReturnCode = pMmioGetHeader( hmmio, &mmImgHdr, sizeof( MMIMAGEHEADER ),
456                                   (PLONG)&ulBytesRead, 0L, 0L );
457
458    if ( ulReturnCode != MMIO_SUCCESS ) {
459         pMmioClose( hmmio, 0L );
460         return NULLHANDLE;
461    }
462
463    dwHeight = mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cy;
464    dwWidth = mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cx;
465    wBitCount = mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cBitCount;
466    dwRowBits = dwWidth * mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cBitCount;
467    dwNumRowBytes = dwRowBits >> 3;
468
469    if ( dwRowBits % 8 ) {
470         dwNumRowBytes++;
471    }
472
473    dwPadBytes = ( dwNumRowBytes % 4 );
474
475    if ( dwPadBytes ) {
476         dwNumRowBytes += 4 - dwPadBytes;
477    }
478
479    pRowBuffer = (PBYTE)malloc( dwNumRowBytes );
480
481    ImageSize.cx = dwWidth;
482    ImageSize.cy = dwHeight;
483
484    *hps = GpiCreatePS( hab, hdc, &ImageSize,
485                        PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC );
486
487    if ( !*hps ) {
488        free( pRowBuffer );
489        pMmioClose( hmmio, 0L );
490        return NULLHANDLE;
491    }
492
493    hbm = GpiCreateBitmap( *hps, &mmImgHdr.mmXDIBHeader.BMPInfoHeader2, 0L, NULL, NULL );
494
495    if ( !hbm )
496    {
497        free( pRowBuffer );
498        pMmioClose( hmmio, 0L );
499        return NULLHANDLE;
500    }
501
502    hbReturnCode = GpiSetBitmap( *hps, hbm );
503
504    for ( dwRowCount = 0; dwRowCount < dwHeight; dwRowCount++ )
505    {
506         ulBytesRead = pMmioRead( hmmio, pRowBuffer, dwNumRowBytes );
507
508         if ( !ulBytesRead ) {
509              break;
510         }
511
512         lReturnCode = GpiSetBitmapBits( *hps, dwRowCount, 1, pRowBuffer,
513                                         (PBITMAPINFO2)&mmImgHdr.mmXDIBHeader.BMPInfoHeader2 );
514    }
515
516    pMmioClose( hmmio, 0L );
517    free( pRowBuffer );
518
519    return hbm;
520}
521
Note: See TracBrowser for help on using the repository browser.