;PACKBITS.SUB - Compress a buffer of data with TIFF Packbits algorithm ; For use with a header file which contains a JMP MAIN, where MAIN is the ; entry label for any program using this routine. ;Calling convention: ; PACKBITS BUFFER,LENGTH ; BUFFER is a pointer to the buffered raw raster data ; LENGTH is the actual length of the raster line, not the buffer size ; Note: Cannot be literal, this value is updated on return! ; stack_buffer=2048 ; packbits macro push #1,#2 call _packbits pop #2 push offset >m1 ret 2 m1: #em ; ;pseudocode: ; lastc=string[0]; ; for (i=1, j=0; i < len; i++) ; c=string[i]; ; if (c == lastc) ; if tiff[j] <= 0 // already in repeat count or only one byte counted ; {tiff[j]--; // repeat counts are negative; repeat count -1 means 2 reps ; // max repeat count is -127; -128 is a NOP (used as EOD by PostScript) ; if (tiff[j] = -128) {tiff[j]++; tiff[++j] = 0; continue;}} ; else // not now in repeat count, so advance to next cell ; // after first decrementing for what we counted as a literal ; {tiff[j]--; tiff[++j]=-1; continue;} ; endif; ; else // c is different from last time ; lastc = c; ; if tiff[j] < 0 // were we in repeat count up to now? ; {tiff[++j]=0; continue;} // if so, advance and clear next cell ; else // already in count of dissimilar bytes ; {tiff[j]++; //max is 127, meaning a string of 128 bytes ; if (tiff[j] < 0) {tiff[j]--; tiff[++j] = 0; continue;}} //if past max ; endif; ; endif; ; endfor; ;note from page 15-22 of PCL 5 Printer Language Reference Manual, Oct. 1992: ;"It is more efficient to code two consecutive identical bytes as a repeated ; byte. If these bytes are preceded and followed by literal bytes, however, ; it is more efficient to code the entire group as literal bytes." ;8/26/95 Make sure when so doing that the count does not exceed 127!!!!! ; // next, compress the tiff control bytes according to the note above ; for (i=1, k=0; i<=j;) ; if (tiff[i] = -1 && tiff[k] > 0 && tiff[i+1] > 0) ; tiff[k] = tiff[k] + tiff[i+1] + 3; i += 2; //leave k where it is ; if tiff[k] > 127 {i -= 2; tiff[k] = tiff[k] - 3 - tiff[i+1]; //undo it ; tiff[++k] = tiff[i++];} // then skip this one ; endif; ; else tiff[++k] = tiff[i++]; ; endif; ; endfor; j = i - 1; // update end to reflect the compression ; // now merge the control string with the raster data ; for (o=0, n=0, m=0; o<=j; o++) ; buffer[o++] = tiff[n++]; // store the control byte and advance pointers ; if tiff[n] < 0 { // repeat count? ; {buffer[o++] = string[m]; m+=(-tiff[n] + 1); continue;} ; else ; for (p = 0; p <= tiff[n]; p++) // copy the literal chars to output ; {buffer[o++] = string[m+p];} ; m += (tiff[n] + 1); // advance pointer into string ; endfor; ; endif; ; endfor; ; _packbits: or ax,ax ;clear carry first call saval ;preserve all registers lea bx,saval_retaddr+2[bp] ;get pointer to args mov cx,[bx] ;raster line length mov si,2[bx] ;buffer address ;we'll avoid using another variable for counting the strings by using 128 ; (80h) as end-of-string marker, since PCL ignores it and PS uses it for ; precisely that purpose... ;Called with CX=bytes of raster data, DI=pointer to raster data. ;Returned with updates in the same registers or cx,cx ;anything for us to do? jz ret ;return if not cmp cx,stack_buffer ;more than max size? call >e1 ;quit if so sub sp,(stack_buffer*2)+2 ;make buffer space on the stack lea di,[bp-((stack_buffer*2)+2)] ;destination, stack buffer... rep movsb ;store the raw data there lea di,[bp-((stack_buffer*2)+2)] ;point to start again mov cx,[bx] ;restore length word lastc = dl c = al zero = ah temp = dh xor ax,ax ;we'll leave AH clear mov dx,ax ;copy zero into last-byte register push cx ;save current count push 2[bx] ;output data pointer push bx ;arg pointer strlen = -((stack_buffer*2)+4) pointer = -((stack_buffer*2)+6) argptr = -((stack_buffer*2)+8) mov si,di ;get it into source pointer for LODSB lea bx,[bp-stack_buffer] ;point to control buffer mov [bx],zero ;clear current location ; lastc=string[0]; lodsb ;load up a data byte dec cx ;decrement the count mov lastc,c ;copy the byte for later comparison ;now, for the length of the data, count SAME bytes and NOT-SAME, ; storing the counts in the array set up for that purpose. ; for (i=1, j=0; i < len; i++) ; c=string[i]; tiff = 0 ;not really, but we save bytes by hiding offset in j j = bx a1: jcxz >a5 ;pack it up when done counting lodsb ;get next byte and advance pointer dec cx ;dec the count ; if (c == lastc) cmp c,lastc ;same as last time? jne >a3 ;skip ahead if not ; if tiff[j] <= 0 // already in repeat count or only one byte counted ; {tiff[j]--; // repeat counts are negative; repeat count -1 means 2 reps mov temp,tiff[j] ;are we already counting repeat bytes? or temp,temp ;or perhaps have only one byte so far in this run? jg >a2 ;skip ahead if not dec byte ptr tiff[j] ;bumps "up" the repeat count ; // max repeat count is -127; -128 is a NOP (used as EOD by PostScript) ; if (tiff[j] = -128) {tiff[j]++; tiff[++j] = 0; continue;}} cmp temp,-127 ;was it most negative value? jne a1 ;loop if not mov tiff[j],temp ;restore value of current pointer... inc j ;advance to next cell... mov tiff[j],zero ;initialize it... jmp a1 ;loop ; else // not now in repeat count, so advance to next cell ; // after first decrementing for what we counted as a literal ; {tiff[j]--; tiff[++j]=-1; continue;} ; endif; a2: dec byte ptr tiff[j] ;we mistakenly counted it first as literal inc j ;point to next cell... mov byte ptr [j],-1 ;store repeat count of 2 jmp short a1 ;loop back around ; else // c is different from last time ; lastc = c; a3: mov lastc,c ;store it for next comparison ; if tiff[j] < 0 // were we in repeat count up to now? ; {tiff[++j]=0; continue;} // if so, advance and clear next cell test byte ptr [j],80h ;is current count a repeat count? je >a4 ;skip if not inc j ;advance to next array element mov tiff[j],zero ;clear it jmp short a1 ;loop ; else // already in count of dissimilar bytes ; {tiff[j]++; //max is 127, meaning a string of 128 bytes a4: inc byte ptr tiff[j] ;up count of literal bytes ; if (tiff[j] < 0) {tiff[j]--; tiff[++j] = 0; continue;}} //if past max jns a1 ;loop if it didn't overflow into high bit dec byte ptr tiff[j] ;else make it 127 inc j ;point to next array element mov tiff[j],zero ;clear it jmp short a1 ;loop ; endif; ; endif; ; endfor; ; // next, compress the tiff control bytes according to the note above a5: inc j ;but first store EOD marker... mov byte ptr tiff[j],80h ;at the end of the control string minus1 = al ;define value tiff = 0 ;not really, but saves a fetch each time i = bx ;we cheat and hide the offset in the integer variables k = di ; for (i=1, k=0; i<=j; i++) lea i,[bp-stack_buffer+1] ;start at offset 1 from start of buffer lea k,[bp-stack_buffer] ;at offset zero to start compressing ; if (tiff[i] = -1 && tiff[k] >= 0 && tiff[i+1] >= 0) mov minus1,-1 ;done with AL, use it for constant a6: cmp tiff[i],minus1 ;check it out... jne >a7 ;move on if not cmp tiff[k],zero ;now test for literals on each side jl >a7 ;move on if not cmp tiff[i+1],zero ;... jl >a7 ;same story ; tiff[k] = tiff[k] + tiff[i+1] + 3; i += 2; //leave k where it is mov temp,tiff[i+1] ;now get following cell value, the count ;of bytes in the string of different bytes add tiff[k],temp ;add that in too add byte ptr tiff[k],3 ;now add 2 for the chars, +1 implied ;implied? just remember: a count of zero implies a 1, so two counts of zero, ; added together, will not reflect the actual status unless you add a one. add i,2 ;skip the two bytes we just merged into tiff[k] ; if tiff[k] > 127 {i -= 2; tiff[k] = tiff[k] - 3 - tiff[i+1]; //undo it ; tiff[++k] = tiff[i++];} // then skip this one ; endif; and tiff[k],minus1 ;see if high bit is set jns a6 ;loop if not sub i,2 ;set everything back the way it was sub byte ptr tiff[k],3 ;... sub tiff[k],temp ;... ;then continue on through to skip this sequence ; else tiff[++k] = tiff[i++]; a7: inc k ;advance source pointer mov temp,tiff[i] ;get tiff[i] mov tiff[k],temp ;store as tiff[k] inc i ;then update i cmp temp,80h ;EOD? continue if so jne a6 ;else loop ; // now merge the control string with the raster data ; for (o=0, n=0, m=0; o<=j; o++) o = di n = bx m = si tmp = al lea m,[bp-((stack_buffer*2)+2)] ;get raster data pointer mov o,pointer[bp] ;now the output buffer pointer lea n,[bp-stack_buffer] ;control string a8: cmp byte ptr tiff[n],80h ;end of the control string? jne >a9 ;continue if not ;1/27/96 found out I should not store the NOP until after ALL data lines ; mov tmp,tiff[n] ;get the NOP ; stosb ;and store it mov cx,o ;store in count register mov o,pointer[bp] ;get count of compressed bytes... sub cx,o ;... pop bx ;get arg pointer mov sp,bp ;restore stack pointer mov [bx],cx ;store updated raster line length ret ; buffer[o++] = tiff[n++]; // store the control byte and advance pointers a9: mov tmp,tiff[n] ;get next control byte inc n ;advance the pointer stosb ;move it into the output string ; if tiff[n] < 0 { // repeat count? or tmp,tmp ;check sign bit jge >a10 ;skip if not set ; {buffer[o++] = string[m]; m+=(-tiff[n] + 1); continue;} mov cx,ax ;else get the count neg cl ;make it positive (just low byte used) ;don't adjust for implied 1, since this MOVSB will advance the ptr... movsb ;only store one of them, however... rep lodsb ;then just skip over the raster data jmp short a8 ;loop back around ; else ; for (p = 0; p <= tiff[n]; p++) // copy the literal chars to output ; {buffer[o++] = string[m+n];} ; m += tiff[n] + 1; // advance pointer into string ; endfor; ; endif; ; endfor; a10: mov cx,ax ;get count as word inc cx ;adjust for implied 1 rep movsb ;store the literal bytes jmp a8 ;loop till EOD byte (128) found e1: jbe ret ;OK if less or same or word saval_flags[bp],carry pop ax ;don't continue ret ;to SAVAL