mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-02 22:17:06 +08:00

* Prepared skeleton and basic component implementations for the jbig2 encoding. * Added Bitset. Implemented Bitmap. * Decoder with old Arithmetic Decoder * Partly working arithmetic * Working arithmetic decoder. * MMR patched. * rebuild to apache. * Working generic * Working generic * Decoded full document * Update Jenkinsfile go version [master] (#398) * Update Jenkinsfile go version * Decoded AnnexH document * Minor issues fixed. * Update README.md * Fixed generic region errors. Added benchmark. Added bitmap unpadder. Added Bitmap toImage method. * Fixed endofpage error * Added integration test. * Decoded all test files without errors. Implemented JBIG2Global. * Merged with v3 version * Fixed the EOF in the globals issue * Fixed the JBIG2 ChocolateData Decode * JBIG2 Added license information * Minor fix in jbig2 encoding. * Applied the logging convention * Cleaned unnecessary imports * Go modules clear unused imports * checked out the README.md * Moved trace to Debug. Fixed the build integrate tag in the document_decode_test.go * Initial encoder skeleton * Applied UniPDF Developer Guide. Fixed lint issues. * Cleared documentation, fixed style issues. * Added jbig2 doc.go files. Applied unipdf guide style. * Minor code style changes. * Minor naming and style issues fixes. * Minor naming changes. Style issues fixed. * Review r11 fixes. * Added JBIG2 Encoder skeleton. * Moved Document and Page to jbig2/document package. Created decoder package responsible for decoding jbig2 stream. * Implemented raster functions. * Added raster uni low test funcitons. * Added raster low test functions * untracked files on jbig2-encoder: c869089 Added raster low test functions * index on jbig2-encoder: c869089 Added raster low test functions * Added morph files. * implemented jbig2 encoder basics * JBIG2 Encoder - Generic method * Added jbig2 image encode ttests, black/white image tests * cleaned and tested jbig2 package * unfinished jbig2 classified encoder * jbig2 minor style changes * minor jbig2 encoder changes * prepared JBIG2 Encoder * Style and lint fixes * Minor changes and lints * Fixed shift unsinged value build errors * Minor naming change * Added jbig2 encode, image gondels. Fixed jbig2 decode bug. * Provided jbig2 core.DecodeGlobals function. * Fixed JBIG2Encoder `r6` revision issues. * Removed public JBIG2Encoder document. * Minor style changes * added NewJBIG2Encoder function. * fixed JBIG2Encoder 'r9' revision issues. * Cleared 'r9' commented code. * Updated ACKNOWLEDGEMENETS. Fixed JBIG2Encoder 'r10' revision issues. Co-authored-by: Gunnsteinn Hall <gunnsteinn.hall@gmail.com>
1723 lines
46 KiB
Go
1723 lines
46 KiB
Go
/*
|
|
* This file is subject to the terms and conditions defined in
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
*/
|
|
|
|
package bitmap
|
|
|
|
import (
|
|
"github.com/unidoc/unipdf/v3/common"
|
|
|
|
"github.com/unidoc/unipdf/v3/internal/jbig2/errors"
|
|
)
|
|
|
|
// RasterOperator is the raster operation flag operator.
|
|
// There are following raster operations:
|
|
// PixClr 0000 0x0
|
|
// PixSet 1111 0xf
|
|
// PixSrc s 1100 0xc
|
|
// PixDst d 1010 0xa
|
|
// PixNotSrc ~s 0011 0x3
|
|
// PixNotDst ~d 0101 0x5
|
|
// PixSrcOrDst s | d 1110 0xe
|
|
// PixSrcAndDst s & d 1000 0x8
|
|
// PixSrcXorDst s ^ d 0110 0x6
|
|
// PixNotSrcOrDst ~s | d 1011 0xb
|
|
// PixNotSrcAndDst ~s & d 0010 0x2
|
|
// PixSrcOrNotDst s | ~d 1101 0xd
|
|
// PixSrcAndNotDst s & ~d 0100 0x4
|
|
// PixNotPixSrcOrDst ~(s | d) 0001 0x1
|
|
// PixNotPixSrcAndDst ~(s & d) 0111 0x7
|
|
// PixNotPixSrcXorDst ~(s ^ d) 1001 0X9
|
|
type RasterOperator int
|
|
|
|
// Raster operator constant definitions.
|
|
const (
|
|
PixSrc RasterOperator = 0xc
|
|
PixDst RasterOperator = 0xa
|
|
PixNotSrc RasterOperator = 0x3
|
|
PixNotDst RasterOperator = 0x5
|
|
|
|
PixClr RasterOperator = 0x0
|
|
PixSet RasterOperator = 0xf
|
|
|
|
PixSrcOrDst RasterOperator = 0xe
|
|
PixSrcAndDst RasterOperator = 0x8
|
|
PixSrcXorDst RasterOperator = 0x6
|
|
PixNotSrcOrDst RasterOperator = 0xb
|
|
PixNotSrcAndDst RasterOperator = 0x2
|
|
PixSrcOrNotDst RasterOperator = 0xd
|
|
PixSrcAndNotDst RasterOperator = 0x4
|
|
PixNotPixSrcOrDst RasterOperator = 0x1
|
|
PixNotPixSrcAndDst RasterOperator = 0x7
|
|
PixNotPixSrcXorDst RasterOperator = 0x9
|
|
|
|
PixPaint = PixSrcOrDst
|
|
PixSubtract = PixNotSrcAndDst
|
|
PixMask = PixSrcAndDst
|
|
)
|
|
|
|
// RasterOperation does the rastering operation on the provided 'dest' bitmap.
|
|
// There are 18 operations, described by the 'op' RasterOperator.
|
|
// The PixDst is a no-op.
|
|
//
|
|
// PixClr, PixSet, PixNotPixDst operate only on the 'dest'.
|
|
//
|
|
// The other 14 involve both 'src' and 'dest' bitmaps and depends on the bit values of either just the src or the
|
|
// both 'src' and 'dest'.
|
|
// Out of these 14 operators there are only 12 unique logical combinations. ~(s) ^ d) == ~(s ^ d) == s ^ ~(d).
|
|
// Parameters:
|
|
// 'dest' 'dest' bitmap
|
|
// 'dx' x val of UL corner of 'dest' bitmap
|
|
// 'dy' y val of UL corner of 'dest' bitmap
|
|
// 'dw' is the width of the operational rectangle on the bitmap 'dest'
|
|
// 'dh' is the height of the operational rectangle on the bitmap 'dest'
|
|
// 'op' raster operator code
|
|
// 'src' 'src' bitmap
|
|
// 'sx' x val of UL corner of 'src' bitmap
|
|
// 'sy' y val of UL corner of 'src' bitmap
|
|
func RasterOperation(dest *Bitmap, dx, dy, dw, dh int, op RasterOperator, src *Bitmap, sx, sy int) error {
|
|
return rasterOperation(dest, dx, dy, dw, dh, op, src, sx, sy)
|
|
}
|
|
|
|
// RasterOperation has the same function as the RasterOperation package function, where the
|
|
// 'b' bitmap is the 'dest'.
|
|
func (b *Bitmap) RasterOperation(dx, dy, dw, dh int, op RasterOperator, src *Bitmap, sx, sy int) error {
|
|
return rasterOperation(b, dx, dy, dw, dh, op, src, sx, sy)
|
|
}
|
|
|
|
func rasterOperation(dest *Bitmap, dx, dy, dw, dh int, op RasterOperator, src *Bitmap, sx, sy int) error {
|
|
const processName = "rasterOperation"
|
|
if dest == nil {
|
|
return errors.Error(processName, "nil 'dest' Bitmap")
|
|
}
|
|
if op == PixDst {
|
|
return nil
|
|
}
|
|
|
|
switch op {
|
|
case PixClr, PixSet, PixNotDst:
|
|
rasterOpUniLow(dest, dx, dy, dw, dh, op)
|
|
return nil
|
|
}
|
|
|
|
if src == nil {
|
|
common.Log.Debug("RasterOperation source bitmap is not defined")
|
|
return errors.Error(processName, "nil 'src' bitmap")
|
|
}
|
|
if err := rasterOpLow(dest, dx, dy, dw, dh, op, src, sx, sy); err != nil {
|
|
return errors.Wrap(err, processName, "")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// rasterOpLow scales width, performs clipping, checks alignment and dispatches for the 'op' RasterOperator.
|
|
func rasterOpLow(dest *Bitmap, dx, dy int, dw, dh int, op RasterOperator, src *Bitmap, sx, sy int) error {
|
|
var dHangW, sHangW, dHangH, sHangH int
|
|
// first, clip horizontally (sx, dx, dw)
|
|
if dx < 0 {
|
|
sx -= dx
|
|
dw += dx
|
|
dx = 0
|
|
}
|
|
if sx < 0 {
|
|
dx -= sx
|
|
dw += sx
|
|
sx = 0
|
|
}
|
|
// rect ovhang dest to right
|
|
dHangW = dx + dw - dest.Width
|
|
if dHangW > 0 {
|
|
dw -= dHangW
|
|
}
|
|
// rect ovhang src to right
|
|
sHangW = sx + dw - src.Width
|
|
if sHangW > 0 {
|
|
dw -= sHangW
|
|
}
|
|
|
|
// clip vertically (sy, dy, dh)
|
|
if dy < 0 {
|
|
sy -= dy
|
|
dh += dy
|
|
dy = 0
|
|
}
|
|
if sy < 0 {
|
|
dy -= sy
|
|
dh += sy
|
|
sy = 0
|
|
}
|
|
|
|
// rect ovhang dest below
|
|
dHangH = dy + dh - dest.Height
|
|
if dHangH > 0 {
|
|
dh -= dHangH
|
|
}
|
|
// rect ovhang src below
|
|
sHangH = sy + dh - src.Height
|
|
if sHangH > 0 {
|
|
dh -= sHangH
|
|
}
|
|
|
|
// quit if clipped entirely
|
|
if dw <= 0 || dh <= 0 {
|
|
return nil
|
|
}
|
|
|
|
// dispatch to aligned or non-aligned blitters.
|
|
var err error
|
|
switch {
|
|
case dx&7 == 0 && sx&7 == 0:
|
|
err = rasterOpByteAlignedLow(dest, dx, dy, dw, dh, op, src, sx, sy)
|
|
case dx&7 == sx&7:
|
|
err = rasterOpVAlignedLow(dest, dx, dy, dw, dh, op, src, sx, sy)
|
|
default:
|
|
err = rasterOpGeneralLow(dest, dx, dy, dw, dh, op, src, sx, sy)
|
|
}
|
|
if err != nil {
|
|
return errors.Wrap(err, "rasterOpLow", "")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// rasterOpByteAlignedLow called when both the src and dest bitmaps are left aligned on 8-bit boundaries - dx & 7 == 0 AND sx & 7 == 0.
|
|
func rasterOpByteAlignedLow(dest *Bitmap, dx, dy, dw, dh int, op RasterOperator, src *Bitmap, sx, sy int) error {
|
|
var (
|
|
// mask for lat partial byte
|
|
lwMask byte
|
|
// index of the first src byte
|
|
psfWord int
|
|
// index of the first dest byte
|
|
pdfWord int
|
|
// line index for source and dest bitmap
|
|
lines, lined int
|
|
i, j int
|
|
)
|
|
// get the number of full bytes
|
|
fullBytesNumber := dw >> 3
|
|
// get the number of ovrhang bits in last partial word
|
|
lwBits := dw & 7
|
|
if lwBits > 0 {
|
|
lwMask = lmaskByte[lwBits]
|
|
}
|
|
psfWord = src.RowStride*sy + (sx >> 3)
|
|
pdfWord = dest.RowStride*dy + (dx >> 3)
|
|
|
|
switch op {
|
|
case PixSrc:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] = src.Data[lines]
|
|
lined++
|
|
lines++
|
|
}
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], src.Data[lines], lwMask)
|
|
}
|
|
}
|
|
case PixNotSrc:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] = ^(src.Data[lines])
|
|
lined++
|
|
lines++
|
|
}
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], ^src.Data[lines], lwMask)
|
|
}
|
|
}
|
|
case PixSrcOrDst:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] |= src.Data[lines]
|
|
lined++
|
|
lines++
|
|
}
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], src.Data[lines]|dest.Data[lined], lwMask)
|
|
}
|
|
}
|
|
case PixSrcAndDst:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] &= src.Data[lines]
|
|
lined++
|
|
lines++
|
|
}
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], src.Data[lines]&dest.Data[lined], lwMask)
|
|
}
|
|
}
|
|
case PixSrcXorDst:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] ^= src.Data[lines]
|
|
lined++
|
|
lines++
|
|
}
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], src.Data[lines]^dest.Data[lined], lwMask)
|
|
}
|
|
}
|
|
case PixNotSrcOrDst:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] |= ^(src.Data[lines])
|
|
lined++
|
|
lines++
|
|
}
|
|
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], ^(src.Data[lines])|dest.Data[lined], lwMask)
|
|
}
|
|
}
|
|
case PixNotSrcAndDst:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] &= ^(src.Data[lines])
|
|
lined++
|
|
lines++
|
|
}
|
|
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], ^(src.Data[lines])&dest.Data[lined], lwMask)
|
|
}
|
|
}
|
|
case PixSrcOrNotDst:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] = src.Data[lines] | ^(dest.Data[lined])
|
|
lined++
|
|
lines++
|
|
}
|
|
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], src.Data[lines]|^(dest.Data[lined]), lwMask)
|
|
}
|
|
}
|
|
case PixSrcAndNotDst:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] = src.Data[lines] & ^(dest.Data[lined])
|
|
lined++
|
|
lines++
|
|
}
|
|
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], src.Data[lines]&^(dest.Data[lined]), lwMask)
|
|
}
|
|
}
|
|
case PixNotPixSrcOrDst:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] = ^(src.Data[lines] | dest.Data[lined])
|
|
lined++
|
|
lines++
|
|
}
|
|
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], ^(src.Data[lines] | dest.Data[lined]), lwMask)
|
|
}
|
|
}
|
|
case PixNotPixSrcAndDst:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] = ^(src.Data[lines] & dest.Data[lined])
|
|
lined++
|
|
lines++
|
|
}
|
|
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], ^(src.Data[lines] & dest.Data[lined]), lwMask)
|
|
}
|
|
}
|
|
case PixNotPixSrcXorDst:
|
|
for i = 0; i < dh; i++ {
|
|
lines = psfWord + i*src.RowStride
|
|
lined = pdfWord + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] = ^(src.Data[lines] ^ dest.Data[lined])
|
|
lined++
|
|
lines++
|
|
}
|
|
|
|
if lwBits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], ^(src.Data[lines] ^ dest.Data[lined]), lwMask)
|
|
}
|
|
}
|
|
default:
|
|
common.Log.Debug("Provided invalid raster operator: %v", op)
|
|
return errors.Error("rasterOpByteAlignedLow", "invalid raster operator")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// rasterOpVAlignedLow called when the left side of the src and dest bitmaps have the same alignment
|
|
// relative to 8-bit boundaries. That is: dx & 7 == sx & 7.
|
|
func rasterOpVAlignedLow(dest *Bitmap, dx, dy, dw, dh int, op RasterOperator, src *Bitmap, sx, sy int) error {
|
|
var (
|
|
// boolean if first dest byte is doubly partial
|
|
dfwPart2B bool
|
|
// boolean if there exist a full dest byte
|
|
dfwFullB bool
|
|
// number of full bytes in dest
|
|
dnFullBytes int
|
|
// index of first full dest byte
|
|
pdfwFull int
|
|
// index of first full src byte
|
|
psfwFull int
|
|
// boolean if last dest byte is partial
|
|
dlwPartB bool
|
|
// mask for last partial dest byte
|
|
dlwMask byte
|
|
// last byte dest bits in ovrhang
|
|
dlwBits int
|
|
// index of last partial dest byte
|
|
pdlwPart int
|
|
// index of last partial src byte
|
|
pslwPart int
|
|
i, j int
|
|
)
|
|
|
|
// first byte dest bits in ovrhang
|
|
dfwBits := 8 - (dx & 7)
|
|
// mask for first partial dest byte
|
|
dfwMask := rmaskByte[dfwBits]
|
|
// index of first partial dest byte
|
|
pdfwPart := dest.RowStride*dy + (dx >> 3)
|
|
// index of first partial src byte
|
|
psfwPart := src.RowStride*sy + (sx >> 3)
|
|
|
|
// is the first word doubly partial?
|
|
if dw < dfwBits {
|
|
dfwPart2B = true
|
|
dfwMask &= lmaskByte[8-dfwBits+dw]
|
|
}
|
|
|
|
// is there a full dest byte?
|
|
if !dfwPart2B {
|
|
dnFullBytes = (dw - dfwBits) >> 3
|
|
if dnFullBytes > 0 {
|
|
// there is a full dest byte
|
|
dfwFullB = true
|
|
pdfwFull = pdfwPart + 1
|
|
psfwFull = psfwPart + 1
|
|
}
|
|
}
|
|
|
|
dlwBits = (dx + dw) & 7
|
|
// is the last word partial?
|
|
if !(dfwPart2B || dlwBits == 0) {
|
|
dlwPartB = true
|
|
dlwMask = lmaskByte[dlwBits]
|
|
pdlwPart = pdfwPart + 1 + dnFullBytes
|
|
pslwPart = psfwPart + 1 + dnFullBytes
|
|
}
|
|
|
|
switch op {
|
|
case PixSrc:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], src.Data[psfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] = src.Data[psfwFull+j]
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], src.Data[pslwPart], dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotSrc:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^src.Data[psfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] = ^src.Data[psfwFull+j]
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^src.Data[pslwPart], dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixSrcOrDst:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], src.Data[psfwPart]|dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] |= src.Data[psfwFull+j]
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], src.Data[pslwPart]|dest.Data[pdlwPart], dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixSrcAndDst:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], src.Data[psfwPart]&dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] &= src.Data[psfwFull+j]
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], src.Data[pslwPart]&dest.Data[pdlwPart], dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixSrcXorDst:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], src.Data[psfwPart]^dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] ^= src.Data[psfwFull+j]
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], src.Data[pslwPart]^dest.Data[pdlwPart], dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotSrcOrDst:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^(src.Data[psfwPart])|dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] |= ^(src.Data[psfwFull+j])
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^(src.Data[pslwPart])|dest.Data[pdlwPart], dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotSrcAndDst:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^(src.Data[psfwPart])&dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] &= ^src.Data[psfwFull+j]
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^(src.Data[pslwPart])&dest.Data[pdlwPart], dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixSrcOrNotDst:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], src.Data[psfwPart]|^(dest.Data[pdfwPart]), dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] = src.Data[psfwFull+j] | ^(dest.Data[pdfwFull+j])
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], src.Data[pslwPart]|^(dest.Data[pdlwPart]), dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixSrcAndNotDst:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], src.Data[psfwPart]&^(dest.Data[pdfwPart]), dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] = src.Data[psfwFull+j] & ^(dest.Data[pdfwFull+j])
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], src.Data[pslwPart]&^(dest.Data[pdlwPart]), dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotPixSrcOrDst:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^(src.Data[psfwPart] | dest.Data[pdfwPart]), dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] = ^(src.Data[psfwFull+j] | dest.Data[pdfwFull+j])
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^(src.Data[pslwPart] | dest.Data[pdlwPart]), dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotPixSrcAndDst:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^(src.Data[psfwPart] & dest.Data[pdfwPart]), dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] = ^(src.Data[psfwFull+j] & dest.Data[pdfwFull+j])
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^(src.Data[pslwPart] & dest.Data[pdlwPart]), dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotPixSrcXorDst:
|
|
// do the first partial byte
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^(src.Data[psfwPart] ^ dest.Data[pdfwPart]), dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
|
|
// do the full bytes
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
dest.Data[pdfwFull+j] = ^(src.Data[psfwFull+j] ^ dest.Data[pdfwFull+j])
|
|
}
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial byte
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^(src.Data[pslwPart] ^ dest.Data[pdlwPart]), dlwMask)
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
default:
|
|
common.Log.Debug("Invalid raster operator: %d", op)
|
|
return errors.Error("rasterOpVAlignedLow", "invalid raster operator")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// rasterOpGeneralLow called when the 'src' and 'dst' rects don't have the same byte alignment.
|
|
func rasterOpGeneralLow(dest *Bitmap, dx, dy, dw, dh int, op RasterOperator, src *Bitmap, sx, sy int) error {
|
|
var (
|
|
// dfwPartB boolean if first dest byte is partial.
|
|
dfwPartB bool
|
|
// dfwPart2B boolean if first dest byte is doubly partial.
|
|
dfwPart2B bool
|
|
// dfwMask mask for first partial 'dest'byte.
|
|
dfwMask byte
|
|
// dfwBits are first byte 'dest' bits in overhang.
|
|
dfwBits int
|
|
// dHang is the 'dest' overhang in first partial byte or if the
|
|
// 'dest' is byte aligned.
|
|
dHang int
|
|
// pdfwPart is an index of the first partial 'dest' byte.
|
|
pdfwPart int
|
|
// psfwPart is an index of the first partial 'src' byte.
|
|
psfwPart int
|
|
|
|
// dfwFullB a boolean if there exists a full 'dest' byte.
|
|
dfwFullB bool
|
|
// dnFullBytes number of full bytes in 'dest'.
|
|
dnFullBytes int
|
|
// pdfwFull is the index of the first full 'dest' byte.
|
|
pdfwFull int
|
|
// psfwFull is the index of the first full 'src' byte.
|
|
psfwFull int
|
|
|
|
// dlwPartB boolean if last 'dest' word is partial.
|
|
dlwPartB bool
|
|
// dlwMask is a mask for the last partial 'dest' byte.
|
|
dlwMask byte
|
|
// dlwBits are the last word 'dest' bits in ovrhang.
|
|
dlwBits int
|
|
// pdlwPart is the index of the last partial 'dest' byte.
|
|
pdlwPart int
|
|
// pslwPart is the index of the last partial 'src' byte.
|
|
pslwPart int
|
|
|
|
// sBytes compose 'src' byte aligned with the 'dest' bytes.
|
|
sBytes byte
|
|
// sfwBits is a first word 'src' bits in overhang, or 8 if src is byte aligned.
|
|
sfwBits int
|
|
// sHang is a 'src' overhang in the first partial byte or 0 if 'src' is word aligned.
|
|
sHang int
|
|
// sleftShift are the bits to shift left for source bytes to align
|
|
// with the 'dest'. Also the number of bits that get shifted to the right to align with the 'dest'.
|
|
sleftShift uint
|
|
// srightShift are the bits to shift right for source byte to align with the 'dest'. Also the number of bits
|
|
// that get shifted to the left to align with the 'dest'.
|
|
srightShift uint
|
|
// srightMask is the mask for selecting sleftshift bits that have been shifted right by srightshift bits.
|
|
srightMask byte
|
|
// shift direction
|
|
sfwShiftDir shift
|
|
// sfwAddB is additional sfw right shift needed
|
|
sfwAddB bool
|
|
// slwAddB is additional slw right shift needed
|
|
slwAddB bool
|
|
i, j int
|
|
)
|
|
|
|
if sx&7 != 0 {
|
|
sHang = 8 - (sx & 7)
|
|
}
|
|
if dx&7 != 0 {
|
|
dHang = 8 - (dx & 7)
|
|
}
|
|
|
|
if sHang == 0 && dHang == 0 {
|
|
srightMask = rmaskByte[0]
|
|
} else {
|
|
if dHang > sHang {
|
|
sleftShift = uint(dHang - sHang)
|
|
} else {
|
|
sleftShift = uint(8 - (sHang - dHang))
|
|
}
|
|
srightShift = 8 - sleftShift
|
|
srightMask = rmaskByte[sleftShift]
|
|
}
|
|
|
|
// is the first dest word partial?
|
|
if (dx & 7) != 0 {
|
|
dfwPartB = true
|
|
dfwBits = 8 - (dx & 7)
|
|
dfwMask = rmaskByte[dfwBits]
|
|
|
|
pdfwPart = dest.RowStride*dy + (dx >> 3)
|
|
psfwPart = src.RowStride*sy + (sx >> 3)
|
|
|
|
sfwBits = 8 - (sx & 7)
|
|
if dfwBits > sfwBits {
|
|
sfwShiftDir = shiftLeft
|
|
if dw >= sHang {
|
|
sfwAddB = true
|
|
}
|
|
} else {
|
|
sfwShiftDir = shiftRight
|
|
}
|
|
}
|
|
|
|
if dw < dfwBits {
|
|
dfwPart2B = true
|
|
dfwMask &= lmaskByte[8-dfwBits+dw]
|
|
}
|
|
|
|
if !dfwPart2B {
|
|
dnFullBytes = (dw - dfwBits) >> 3
|
|
if dnFullBytes != 0 {
|
|
dfwFullB = true
|
|
pdfwFull = dest.RowStride*dy + ((dx + dHang) >> 3)
|
|
psfwFull = src.RowStride*sy + ((sx + dHang) >> 3)
|
|
}
|
|
}
|
|
|
|
dlwBits = (dx + dw) & 7
|
|
if !(dfwPart2B || dlwBits == 0) {
|
|
dlwPartB = true
|
|
dlwMask = lmaskByte[dlwBits]
|
|
pdlwPart = dest.RowStride*dy + ((dx + dHang) >> 3) + dnFullBytes
|
|
pslwPart = src.RowStride*sy + ((sx + dHang) >> 3) + dnFullBytes
|
|
|
|
if dlwBits > int(srightShift) {
|
|
slwAddB = true
|
|
}
|
|
}
|
|
|
|
switch op {
|
|
case PixSrc:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], sBytes, dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] = sBytes
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], sBytes, dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotSrc:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^sBytes, dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] = ^sBytes
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^sBytes, dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixSrcOrDst:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], sBytes|dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] |= sBytes
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], sBytes|dest.Data[pdlwPart], dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixSrcAndDst:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], sBytes&dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] &= sBytes
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], sBytes&dest.Data[pdlwPart], dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixSrcXorDst:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], sBytes^dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] ^= sBytes
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], sBytes^dest.Data[pdlwPart], dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotSrcOrDst:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^sBytes|dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] |= ^sBytes
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^sBytes|dest.Data[pdlwPart], dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotSrcAndDst:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^sBytes&dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] &= ^sBytes
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^sBytes&dest.Data[pdlwPart], dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixSrcOrNotDst:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], sBytes|^dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] = sBytes | ^dest.Data[pdfwFull+j]
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], sBytes|^dest.Data[pdlwPart], dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixSrcAndNotDst:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], sBytes & ^dest.Data[pdfwPart], dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] = sBytes & ^dest.Data[pdfwFull+j]
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], sBytes & ^dest.Data[pdlwPart], dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotPixSrcOrDst:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^(sBytes | dest.Data[pdfwPart]), dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] = ^(sBytes | dest.Data[pdfwFull+j])
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^(sBytes | dest.Data[pdlwPart]), dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotPixSrcAndDst:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^(sBytes & dest.Data[pdfwPart]), dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] = ^(sBytes & dest.Data[pdfwFull+j])
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^(sBytes & dest.Data[pdlwPart]), dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
case PixNotPixSrcXorDst:
|
|
// do the first partial word
|
|
if dfwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
if sfwShiftDir == shiftLeft {
|
|
sBytes = src.Data[psfwPart] << sleftShift
|
|
if sfwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[psfwPart+1]>>srightShift, srightMask)
|
|
}
|
|
} else {
|
|
sBytes = src.Data[psfwPart] >> srightShift
|
|
}
|
|
|
|
dest.Data[pdfwPart] = combinePartial(dest.Data[pdfwPart], ^(sBytes ^ dest.Data[pdfwPart]), dfwMask)
|
|
pdfwPart += dest.RowStride
|
|
psfwPart += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the full words
|
|
if dfwFullB {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < dnFullBytes; j++ {
|
|
sBytes = combinePartial(src.Data[psfwFull+j]<<sleftShift, src.Data[psfwFull+j+1]>>srightShift, srightMask)
|
|
dest.Data[pdfwFull+j] = ^(sBytes ^ dest.Data[pdfwFull+j])
|
|
}
|
|
|
|
pdfwFull += dest.RowStride
|
|
psfwFull += src.RowStride
|
|
}
|
|
}
|
|
|
|
// do the last partial word
|
|
if dlwPartB {
|
|
for i = 0; i < dh; i++ {
|
|
sBytes = src.Data[pslwPart] << sleftShift
|
|
if slwAddB {
|
|
sBytes = combinePartial(sBytes, src.Data[pslwPart+1]>>srightShift, srightMask)
|
|
}
|
|
dest.Data[pdlwPart] = combinePartial(dest.Data[pdlwPart], ^(sBytes ^ dest.Data[pdlwPart]), dlwMask)
|
|
|
|
pdlwPart += dest.RowStride
|
|
pslwPart += src.RowStride
|
|
}
|
|
}
|
|
default:
|
|
common.Log.Debug("Operation: '%d' not permitted", op)
|
|
return errors.Error("rasterOpGeneralLow", "raster operation not permitted")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// rasterOpUniLow performs clipping, checks aligment and dispatches for the rasterop.
|
|
func rasterOpUniLow(dest *Bitmap, dx, dy, dw, dh int, op RasterOperator) {
|
|
// clip horizontally (dx, dw)
|
|
if dx < 0 {
|
|
dw += dx
|
|
dx = 0
|
|
}
|
|
|
|
dHangw := dx + dw - dest.Width
|
|
if dHangw > 0 {
|
|
// reduce dw
|
|
dw -= dHangw
|
|
}
|
|
|
|
// clip vertically(dy, dh)
|
|
if dy < 0 {
|
|
dh += dy
|
|
dy = 0
|
|
}
|
|
|
|
dHangh := dy + dh - dest.Height
|
|
if dHangh > 0 {
|
|
// reduce dh
|
|
dh -= dHangh
|
|
}
|
|
|
|
// if clipped entirely quit
|
|
if dw <= 0 || dh <= 0 {
|
|
return
|
|
}
|
|
|
|
if (dx & 7) == 0 {
|
|
rasterOpUniWordAlignedLow(dest, dx, dy, dw, dh, op)
|
|
} else {
|
|
rasterOpUniGeneralLow(dest, dx, dy, dw, dh, op)
|
|
}
|
|
}
|
|
|
|
// rasterOpUniWordAligneLow is called when the 'dest' bitmap is left aligned. This means dx & 7 == 0.
|
|
func rasterOpUniWordAlignedLow(dest *Bitmap, dx, dy int, dw, dh int, op RasterOperator) {
|
|
var (
|
|
// firstWordIndex is the byte index of the first byte
|
|
firstWordIndex int
|
|
// lwMask is the mask for the last partial byte
|
|
lwmask byte
|
|
i, j int
|
|
lined int
|
|
)
|
|
// fullBytesNumber is the number of Full bytes.
|
|
fullBytesNumber := dw >> 3
|
|
// lwBits is the number of ovrhang bits in last partial byte.
|
|
lwbits := dw & 7
|
|
if lwbits > 0 {
|
|
lwmask = lmaskByte[lwbits]
|
|
}
|
|
|
|
firstWordIndex = dest.RowStride*dy + (dx >> 3)
|
|
|
|
switch op {
|
|
case PixClr:
|
|
for i = 0; i < dh; i++ {
|
|
lined = firstWordIndex + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] = 0x0
|
|
lined++
|
|
}
|
|
if lwbits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], 0x0, lwmask)
|
|
}
|
|
}
|
|
case PixSet:
|
|
for i = 0; i < dh; i++ {
|
|
lined = firstWordIndex + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] = 0xff
|
|
lined++
|
|
}
|
|
if lwbits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], 0xff, lwmask)
|
|
}
|
|
}
|
|
case PixNotDst:
|
|
for i = 0; i < dh; i++ {
|
|
lined = firstWordIndex + i*dest.RowStride
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[lined] = ^dest.Data[lined]
|
|
lined++
|
|
}
|
|
if lwbits > 0 {
|
|
dest.Data[lined] = combinePartial(dest.Data[lined], ^dest.Data[lined], lwmask)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// rasterOpUniGeneralLow static low-level uni rasterop without byte aligment.
|
|
func rasterOpUniGeneralLow(dest *Bitmap, dx, dy int, dw, dh int, op RasterOperator) {
|
|
var (
|
|
// fbDoublyPartial boolean if first 'dest' byte is doubly partial.
|
|
fbDoublyPartial bool
|
|
// fbFull boolean if there exists a full 'dest' byte word.
|
|
fbFull bool
|
|
// fullBytesNumber is the number of full 'dest' bytes.
|
|
fullBytesNumber int
|
|
// firstFullByteIndex is an index in 'dest'.Data' of the first full byte.
|
|
firstFullByteIndex int
|
|
|
|
// lbBits last byte 'dest' bits in ovrhang.
|
|
lbBits int
|
|
// lastBytePartialIndex is an index in 'dest'.Data of the last partial byte.
|
|
lastBytePartialIndex int
|
|
// lbPartial boolean if last 'dest' byte is partial
|
|
lbPartial bool
|
|
// lbMask mask for the last partial 'dest' byte.
|
|
lbMask byte
|
|
)
|
|
// fbBits first byte 'dest' bits int ovrhang.
|
|
fbBits := 8 - (dx & 7)
|
|
// fbMask is a mask for first partial 'dest' byte.
|
|
fbMask := rmaskByte[fbBits]
|
|
// firstBytePartialIndex is an index in 'dest'.Data of the first partial byte.
|
|
firstBytePartialIndex := dest.RowStride*dy + (dx >> 3)
|
|
|
|
// is the first byte is doubly partial?
|
|
if dw < fbBits {
|
|
fbDoublyPartial = true
|
|
fbMask &= lmaskByte[8-fbBits+dw]
|
|
}
|
|
|
|
// is there a full 'dest' byte?
|
|
if !fbDoublyPartial {
|
|
fullBytesNumber = (dw - fbBits) >> 3
|
|
if fullBytesNumber != 0 {
|
|
fbFull = true
|
|
firstFullByteIndex = firstBytePartialIndex + 1
|
|
}
|
|
}
|
|
|
|
// is the last byte partial?
|
|
lbBits = (dx + dw) & 7
|
|
if !(fbDoublyPartial || lbBits == 0) {
|
|
lbPartial = true
|
|
lbMask = lmaskByte[lbBits]
|
|
lastBytePartialIndex = firstBytePartialIndex + 1 + fullBytesNumber
|
|
}
|
|
|
|
var i, j int
|
|
switch op {
|
|
case PixClr:
|
|
// do the first partial word
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[firstBytePartialIndex] = combinePartial(dest.Data[firstBytePartialIndex], 0x0, fbMask)
|
|
firstBytePartialIndex += dest.RowStride
|
|
}
|
|
|
|
// do the full words
|
|
if fbFull {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[firstFullByteIndex+j] = 0x0
|
|
}
|
|
firstFullByteIndex += dest.RowStride
|
|
}
|
|
}
|
|
|
|
// last partial word
|
|
if lbPartial {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[lastBytePartialIndex] = combinePartial(dest.Data[lastBytePartialIndex], 0x0, lbMask)
|
|
lastBytePartialIndex += dest.RowStride
|
|
}
|
|
}
|
|
case PixSet:
|
|
// do the first partial word
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[firstBytePartialIndex] = combinePartial(dest.Data[firstBytePartialIndex], 0xff, fbMask)
|
|
firstBytePartialIndex += dest.RowStride
|
|
}
|
|
|
|
// do the full words
|
|
if fbFull {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[firstFullByteIndex+j] = 0xff
|
|
}
|
|
firstFullByteIndex += dest.RowStride
|
|
}
|
|
}
|
|
|
|
// last partial word
|
|
if lbPartial {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[lastBytePartialIndex] = combinePartial(dest.Data[lastBytePartialIndex], 0xff, lbMask)
|
|
lastBytePartialIndex += dest.RowStride
|
|
}
|
|
}
|
|
case PixNotDst:
|
|
// do the first partial word
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[firstBytePartialIndex] = combinePartial(dest.Data[firstBytePartialIndex], ^dest.Data[firstBytePartialIndex], fbMask)
|
|
firstBytePartialIndex += dest.RowStride
|
|
}
|
|
|
|
// do the full words
|
|
if fbFull {
|
|
for i = 0; i < dh; i++ {
|
|
for j = 0; j < fullBytesNumber; j++ {
|
|
dest.Data[firstFullByteIndex+j] = ^(dest.Data[firstFullByteIndex+j])
|
|
}
|
|
firstFullByteIndex += dest.RowStride
|
|
}
|
|
}
|
|
|
|
// last partial word
|
|
if lbPartial {
|
|
for i = 0; i < dh; i++ {
|
|
dest.Data[lastBytePartialIndex] = combinePartial(dest.Data[lastBytePartialIndex], ^dest.Data[lastBytePartialIndex], lbMask)
|
|
lastBytePartialIndex += dest.RowStride
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var (
|
|
lmaskByte = []byte{
|
|
// 00000000
|
|
0x00,
|
|
// 10000000
|
|
0x80,
|
|
// 11000000
|
|
0xC0,
|
|
// 11100000
|
|
0xE0,
|
|
// 11110000
|
|
0xF0,
|
|
// 11111000
|
|
0xF8,
|
|
// 11111100
|
|
0xFC,
|
|
// 11111110
|
|
0xFE,
|
|
// 11111111
|
|
0xFF,
|
|
}
|
|
rmaskByte = []byte{
|
|
// 00000000
|
|
0x00,
|
|
// 00000001
|
|
0x01,
|
|
// 00000011
|
|
0x03,
|
|
// 00000111
|
|
0x07,
|
|
// 00001111
|
|
0x0F,
|
|
// 00011111
|
|
0x1F,
|
|
// 00111111
|
|
0x3F,
|
|
// 01111111
|
|
0x7F,
|
|
// 11111111
|
|
0xFF,
|
|
}
|
|
)
|
|
|
|
func combinePartial(d, s, m byte) byte {
|
|
return (d & ^(m)) | (s & m)
|
|
}
|
|
|
|
type shift int
|
|
|
|
const (
|
|
shiftLeft shift = iota
|
|
shiftRight
|
|
)
|