You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

150 lines
4.0 KiB

// Copyright 2026 Axel Waggershauser
// SPDX-License-Identifier: Apache-2.0
package zxingcpp
/*
#include "zxingcgo.h"
*/
import "C"
import (
"fmt"
"image"
"image/draw"
"unsafe"
)
// ImageView is a non-owning view into pixel data used for barcode detection.
//
// ImageView keeps a reference to the original Go data to prevent garbage collection.
// Call Close when you are done.
type ImageView struct {
ptr *C.ZXing_ImageView
goData []byte // keeps Go slice alive
}
func NewImageView(data []byte, width, height int, format ImageFormat, rowStride, pixStride int) (*ImageView, error) {
if len(data) == 0 {
return nil, fmt.Errorf("image data is empty")
}
iv := C.ZXing_ImageView_new_checked(
(*C.uint8_t)(unsafe.Pointer(unsafe.SliceData(data))),
C.int(len(data)),
C.int(width),
C.int(height),
C.ZXing_ImageFormat(format),
C.int(rowStride),
C.int(pixStride),
)
if iv == nil {
return nil, zxLastError()
}
return &ImageView{ptr: iv, goData: data}, nil
}
// NewImageViewFromC wraps a pointer to C-allocated memory.
//
// The provided memory must remain valid for as long as the ImageView is used.
// The wrapper will not free the buffer.
func NewImageViewFromC(data unsafe.Pointer, dataLen int, width, height int, format ImageFormat, rowStride, pixStride int) (*ImageView, error) {
iv := C.ZXing_ImageView_new_checked(
(*C.uint8_t)(data),
C.int(dataLen),
C.int(width),
C.int(height),
C.ZXing_ImageFormat(format),
C.int(rowStride),
C.int(pixStride),
)
if iv == nil {
return nil, zxLastError()
}
return &ImageView{ptr: iv}, nil
}
// NewImageViewFromImage constructs an ImageView from a standard library image.Image.
//
// The function maps common concrete image layouts directly to matching ZXing image formats.
// For unsupported layouts it falls back to a grayscale conversion.
func NewImageViewFromImage(src image.Image) (*ImageView, error) {
b := src.Bounds()
width, height := b.Dx(), b.Dy()
switch img := src.(type) {
case *image.Gray:
off := img.PixOffset(b.Min.X, b.Min.Y)
return NewImageView(img.Pix[off:], width, height, ImageFormatLum, img.Stride, 1)
case *image.RGBA:
off := img.PixOffset(b.Min.X, b.Min.Y)
return NewImageView(img.Pix[off:], width, height, ImageFormatRGBA, img.Stride, 4)
case *image.NRGBA:
off := img.PixOffset(b.Min.X, b.Min.Y)
return NewImageView(img.Pix[off:], width, height, ImageFormatRGBA, img.Stride, 4)
case *image.YCbCr:
off := img.YOffset(b.Min.X, b.Min.Y)
return NewImageView(img.Y[off:], width, height, ImageFormatLum, img.YStride, 1)
}
gray := image.NewGray(b)
draw.Draw(gray, b, src, b.Min, draw.Src)
off := gray.PixOffset(b.Min.X, b.Min.Y)
return NewImageView(gray.Pix[off:], width, height, ImageFormatLum, gray.Stride, 1)
}
func (iv *ImageView) Close() error {
if iv == nil || iv.ptr == nil {
return nil
}
C.ZXing_ImageView_delete(iv.ptr)
iv.ptr = nil
iv.goData = nil
return nil
}
// Crop crops the ImageView in-place to the specified rectangle.
func (iv *ImageView) Crop(left, top, width, height int) {
if iv == nil || iv.ptr == nil {
return
}
C.ZXing_ImageView_crop(iv.ptr, C.int(left), C.int(top), C.int(width), C.int(height))
}
// Rotate rotates the ImageView in-place by the specified degrees. Supported values are 0, 90, 180 and 270.
func (iv *ImageView) Rotate(degree int) {
if iv == nil || iv.ptr == nil {
return
}
C.ZXing_ImageView_rotate(iv.ptr, C.int(degree))
}
func zxImage2Go(cimg *C.ZXing_Image) (image.Image, error) {
if cimg == nil {
return nil, zxLastError()
}
defer C.ZXing_Image_delete(cimg)
width := int(C.ZXing_Image_width(cimg))
height := int(C.ZXing_Image_height(cimg))
format := ImageFormat(C.ZXing_Image_format(cimg))
ptr := C.ZXing_Image_data(cimg)
if ptr == nil {
return nil, fmt.Errorf("ZXing_Image_data() returned NULL")
}
if format != ImageFormatLum {
return nil, fmt.Errorf("unsupported writer image format %v", format)
}
// Writer output is grayscale (Lum) with tightly packed rows.
return &image.Gray{
Pix: C.GoBytes(unsafe.Pointer(ptr), C.int(width*height)),
Stride: width,
Rect: image.Rect(0, 0, width, height),
}, nil
}