#include "scanned_img.h" #include "sane_helper.h" #include ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // extern sane_helper* sane_helper_; namespace local_trans { static const char* (*invoke_to_default_language)(const char* str, bool* ok) = nullptr; static const char* (*invoke_from_default_language)(const char* str, bool* ok) = nullptr; int load_lang_pak(const char* dll) { HMODULE h = LoadLibraryExA(dll, 0, RTLD_NOW); if(!h) return GetLastError(); FARPROC *pfn = (FARPROC*)&invoke_from_default_language; *pfn = GetProcAddress(h, "from_default_language"); pfn = (FARPROC*)&invoke_to_default_language; *pfn = GetProcAddress(h, "to_default_language"); return invoke_to_default_language && invoke_to_default_language ? 0 : ENOENT; } std::string lang_trans_between_hz936(const char* in, bool from_hz) { std::string a(lang_trans_between_hz936(in, from_hz, nullptr)); return std::move(a); } const char* lang_trans_between_hz936(const char* in, bool from_hz, void* param) { if(from_hz && invoke_from_default_language) return invoke_from_default_language(in, nullptr); else if(!from_hz && invoke_to_default_language) return invoke_to_default_language(in, nullptr); else return in; } const char* to_default_language(const char* str, bool* ok) { if(invoke_to_default_language) return invoke_to_default_language(str, ok); if(ok) *ok = false; return str; } const char* from_default_language(const char* str, bool* ok) { if(invoke_from_default_language) return invoke_from_default_language(str, ok); if(ok) *ok = false; return str; } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // class refer refer::refer() : ref_(1) {} refer::~refer() {} COM_API_IMPLEMENT(refer, long, add_ref(void)) { std::lock_guard locker(lock_); return ++ref_; } COM_API_IMPLEMENT(refer, long, release(void)) { long ref = 0; { std::lock_guard locker(lock_); ref = --ref_; } if (ref == 0) delete this; return ref; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // class image_buf const unsigned int max_mem_block = 2 * 1024 * 1024; extern void log_info(const char* info, int level); image_buf::image_buf() : bytes_(0), offset_(0), mapped_bytes_(0), is_mem_(true), buf_(nullptr) {} image_buf::~image_buf() { close(); } void image_buf::close(void) { if(buf_) { free(buf_); buf_ = nullptr; } bytes_ = offset_ = mapped_bytes_ = 0; is_mem_ = true; } unsigned char* image_buf::allocate(const char* file, unsigned long long size, bool force_file) { close(); if(force_file) return nullptr; buf_ = (unsigned char*)malloc(size); if(buf_) { memset(buf_, 0, size); bytes_ = mapped_bytes_ = size; } return buf_; } bool image_buf::save(const void* data, size_t* bytes, unsigned long long off) { if(off + *bytes <= mapped_bytes_) { memcpy(buf_ + off, data, *bytes); //if(bytes_ < off + *bytes) // bytes_ = off + *bytes; return true; } else { if(off < mapped_bytes_) { memcpy(buf_ + off, data, mapped_bytes_ - off); *bytes = mapped_bytes_ - off; } else *bytes = 0; //if(bytes_ < off + *bytes) // bytes_ = off + *bytes; return false; } } bool image_buf::save(unsigned long long off, image_buf* src) { if(off + src->bytes() > mapped_bytes_) return false; memcpy(buf_ + off, src->buf_, src->bytes()); //if(bytes_ < off + src->bytes()) // bytes_ = off + src->bytes(); return true; } unsigned long long image_buf::bytes(void) { return bytes_; } unsigned long long image_buf::size(void) { return mapped_bytes_; } unsigned char* image_buf::buffer(unsigned long long off, unsigned int* bytes) { if(off < mapped_bytes_) { if(*bytes > mapped_bytes_ - off) *bytes = mapped_bytes_ - off; return buf_ + off; } return nullptr; } int image_buf::read(void* buf, size_t* bytes, unsigned long long off) { if (!bytes) return SCANNER_ERR_INVALID_PARAMETER; if(off >= bytes_) return SCANNER_ERR_OUT_OF_RANGE; if(*bytes > bytes_ - off) { *bytes = bytes_ - off; } memcpy(buf, buf_ + off, *bytes); return SCANNER_ERR_OK; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // class scanned_img scanned_img::scanned_img(SANE_Handle dev, SANE_Parameters head, int dpi , const char* tmp_file, twain_xfer xfer , SANE_FinalImgFormat *fmt) : head_(head), dpi_(dpi), header_size_(0), file_(tmp_file ? tmp_file : "") , dev_(dev), status_(SANE_Image_Statu_OK) { if (fmt) fmt_ = *fmt; else { fmt_.img_format = SANE_IMAGE_TYPE_BMP; fmt_.detail = 0; } size_t bytes = line_bytes() * height(); std::string h(file_header(fmt_.img_format, (float)dpi, xfer)); unsigned char* dst = NULL; bool ok = false; data_ = new image_buf(); header_size_ = h.length(); dst = data_->allocate(tmp_file, bytes + h.length()); if (dst) { unsigned long long off = 0, total = 0; bytes = h.length(); if (data_->save(h.c_str(), &bytes, off)) { unsigned int len = line_bytes(); unsigned long long line = line_bytes(); if (xfer == TWAIN_XFER_Memory) line *= -1; else off = data_->bytes() - line; dst = data_->buffer(off, &len); int want_to_read = head_.bytes_per_line, rcv = 0, dif = line_bytes() - head_.bytes_per_line; while (dst) { int r = want_to_read > (int)(len) - rcv ? (int)(len)- rcv : want_to_read; int ret = sane_helper_->invoke_sane_read(dev, dst + rcv, r, &r); total += r; if (ret != SANE_STATUS_GOOD) break; want_to_read -= r; rcv += r; if (want_to_read == 0) { want_to_read = head_.bytes_per_line; off -= line; len = line_bytes(); rcv = 0; dst = data_->buffer(off, &len); total += dif; } else { len = want_to_read; dst = data_->buffer(off + rcv, &len); } } ok = total + h.length() + dif == data_->bytes(); } } do_result(ok, xfer); } scanned_img::scanned_img(SANE_Handle dev, SANE_Parameters head, void* data, unsigned int len, int dpi, const char* tmp_file , twain_xfer xfer, SANE_FinalImgFormat* fmt) : head_(head), dpi_(dpi), header_size_(0) , file_(tmp_file ? tmp_file : ""), dev_(dev), status_(SANE_Image_Statu_OK) { if (fmt) fmt_ = *fmt; else { fmt_.img_format = SANE_IMAGE_TYPE_BMP; fmt_.detail = 0; } size_t bytes = line_bytes() * head.lines; std::string h(file_header(fmt_.img_format, (float)dpi, xfer)); unsigned char* dst = NULL, *src = (unsigned char*)data; bool ok = false; header_size_ = h.length(); data_ = new image_buf(); bytes += header_size_; dst = data_->allocate(tmp_file, bytes); bytes = h.length(); if (data_->save(h.c_str(), &bytes, 0)) { unsigned long long off = bytes, line_l = line_bytes(); unsigned int buf_len = line_bytes(), row = 0; if (xfer == TWAIN_XFER_Memory) line_l *= -1; else off = data_->size() - line_l; for (; row < (unsigned int)head.lines; ++row) { bytes = head.bytes_per_line; if (!data_->save(src, &bytes, off)) break; off -= line_l; src += head.bytes_per_line; } ok = row == head.lines; } do_result(ok, xfer); } scanned_img::~scanned_img() { if (data_) delete data_; } void scanned_img::set_image_status(SANE_Image_Statu status) { status_ = status; } std::string scanned_img::file_header(SANE_ImageType type, float resolution, twain_xfer xfer) { std::string h(""); if (type == SANE_IMAGE_TYPE_BMP /*&& xfer != TWAIN_XFER_Memory*/) { BITMAPINFOHEADER bih = { 0 }; int pal_size = 0; bih.biSize = sizeof(bih); bih.biWidth = width(); bih.biBitCount = depth(); bih.biSizeImage = line_bytes() * height(); bih.biPlanes = 1; bih.biHeight = height(); bih.biCompression = BI_RGB; bih.biXPelsPerMeter = bih.biYPelsPerMeter = (LONG)(resolution * 39.37f + .5f); if (bih.biBitCount == 1) pal_size = 2 * sizeof(int); else if (bih.biBitCount == 8) pal_size = 256 * sizeof(int); /*if (xfer == TWAIN_XFER_File)*/ { BITMAPFILEHEADER fh = { 0 }; fh.bfType = MAKEWORD('B', 'M'); fh.bfSize = sizeof(fh) + bih.biSizeImage + sizeof(bih) + pal_size; fh.bfOffBits = sizeof(fh) + sizeof(bih) + pal_size; h = std::string((char*)&fh, sizeof(fh)); } h += std::string((char*)&bih, sizeof(bih)); pal_size_ = pal_size; if (bih.biBitCount == 1) { int pal[] = { 0, 0x0ffffff }; h += std::string((char*)pal, pal_size); } else if (bih.biBitCount == 8) { static unsigned int g_bmp8_pallete[256] = { 0 }; if (g_bmp8_pallete[1] == 0) { for (int i = 1; i < _countof(g_bmp8_pallete); ++i) g_bmp8_pallete[i] = MAKELONG(MAKEWORD(i, i), MAKEWORD(i, 0)); } h += std::string((char*)g_bmp8_pallete, pal_size); } } return h; } void scanned_img::do_result(bool ok, twain_xfer xfer) { if (ok) { if (fmt_.img_format == SANE_IMAGE_TYPE_BMP && fmt_.compress.compression == SANE_COMPRESSION_GROUP4 && xfer == TWAIN_XFER_Memory) { // convert to black-white ... std::string head(file_header(SANE_IMAGE_TYPE_BMP, (float)dpi_, TWAIN_XFER_File)); size_t size = head.length(); image_buf* buf = new image_buf(); std::string file(file_ + ".tmp"); unsigned long long off = 0; if (buf->allocate(file.c_str(), size + data_->bytes(), false) && buf->save(head.c_str(), &size, off)) { if (buf->save(size, data_)) { SANE_ImageFormatConvert conv; std::string sf(file); SANE_Int after = 0; conv.src.data_len = buf->bytes(); conv.src.data = (SANE_String_Const)buf->buffer(0, &conv.src.data_len); conv.src.fmt.img_format = SANE_IMAGE_TYPE_BMP; conv.src.fmt.compress.compression = SANE_COMPRESSION_NONE; conv.src.is_file = SANE_FALSE; conv.dst.data = NULL; conv.dst.data_len = 0; conv.dst.fmt.img_format = SANE_IMAGE_TYPE_BMP; conv.dst.fmt.compress.compression = SANE_COMPRESSION_GROUP4; conv.dst.fmt.compress.detail = NULL; conv.dst.is_file = false; if (sane_helper_->invoke_sane_control_option(dev_, SANE_OPT_ID_TRANSFORM_IMAGE_FORMAT, SANE_ACTION_SET_VALUE, &conv, &after) == SANE_STATUS_GOOD) { delete data_; data_ = new image_buf(); size = conv.dst.data_len; data_->allocate(file_.c_str(), conv.dst.data_len); data_->save(conv.dst.data, &size, 0); sane_helper_->invoke_sane_control_option(dev_, SANE_OPT_ID_FREE_BUFFER, SANE_ACTION_SET_VALUE, (void*)&conv.dst.data, &after); head_.format = SANE_FRAME_GRAY; head_.depth = 1; head_.bytes_per_line = (head_.pixels_per_line + 7) / 8; } } } if (buf) delete buf; } else if (fmt_.img_format == SANE_IMAGE_TYPE_BMP && channel() == 3 && xfer != TWAIN_XFER_Memory) { // swap RGB swap_rgb(); } } else { delete data_; data_ = NULL; header_size_ = 0; } } void scanned_img::swap_rgb(void) { unsigned long long off = 0; unsigned int line = line_bytes(), len = line; unsigned char* dst = NULL; for (int i = 0; i < height(); ++i) { int l = head_.bytes_per_line, cur = 0; off = i * line + header_size_; while (l > 0) { len = l; dst = data_->buffer(off + cur, &len); if (!dst) break; if (len > (unsigned int)l) len = l; len /= 3; for (int pos = 0; pos < (int)len; ++pos) { unsigned char uc = dst[pos * 3 + 0]; dst[pos * 3 + 0] = dst[pos * 3 + 2]; dst[pos * 3 + 2] = uc; } l -= len * 3; cur += len * 3; } if (!dst) break; } } // IRef COM_API_IMPLEMENT(scanned_img, long, add_ref(void)) { return refer::add_ref(); } COM_API_IMPLEMENT(scanned_img, long, release(void)) { return refer::release(); } // IScanImg COM_API_IMPLEMENT(scanned_img, int, width(void)) { return head_.pixels_per_line; } COM_API_IMPLEMENT(scanned_img, int, line_bytes(void)) { if (fmt_.img_format == SANE_IMAGE_TYPE_BMP && head_.depth >= 8) return (head_.bytes_per_line + 3) / 4 * 4; else return head_.bytes_per_line; } COM_API_IMPLEMENT(scanned_img, int, height(void)) { return head_.lines; } COM_API_IMPLEMENT(scanned_img, int, depth(void)) { if (head_.format == SANE_FRAME_RGB) return head_.depth * 3; else return head_.depth; } COM_API_IMPLEMENT(scanned_img, int, channel(void)) { return head_.format == SANE_FRAME_RGB ? 3 : 1; } COM_API_IMPLEMENT(scanned_img, int, dpi(void)) { return dpi_; } COM_API_IMPLEMENT(scanned_img, SANE_Frame, type(void)) { return head_.format; } COM_API_IMPLEMENT(scanned_img, unsigned int, bytes(void)) { return data_ ? (unsigned int)data_->bytes() : 0; } COM_API_IMPLEMENT(scanned_img, unsigned int, header_size(void)) { return header_size_; } COM_API_IMPLEMENT(scanned_img, unsigned char*, data(unsigned long long off, unsigned int* bytes)) { return data_ ? data_->buffer(off, bytes) : NULL; } COM_API_IMPLEMENT(scanned_img, int, read(void* buf, size_t* bytes, unsigned long long off)) { return data_ ? data_->read(buf, bytes, off) : SCANNER_ERR_NO_DATA; } COM_API_IMPLEMENT(scanned_img, const char*, file(void)) { return ""; } COM_API_IMPLEMENT(scanned_img, void, keep_file(bool keep)) { } COM_API_IMPLEMENT(scanned_img, void, copy_header(SANE_Parameters* head)) { *head = head_; } COM_API_IMPLEMENT(scanned_img, int, image_status(void)) { return status_; } COM_API_IMPLEMENT(scanned_img, size_t, get_bits_offset(void)) { if (fmt_.img_format == SANE_IMAGE_TYPE_BMP) return sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + pal_size_; else return 0; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // class safe_img_queue safe_img_queue::safe_img_queue() {} safe_img_queue::~safe_img_queue() { clear(); } void safe_img_queue::access_image(scanned_img* img) { img->add_ref(); } void safe_img_queue::free_image(scanned_img* img) { img->release(); } bool safe_img_queue::get_header(SANE_Parameters* header, size_t* bytes, int* dpi) { scanned_img *img = take(false, &safe_img_queue::access_image); bool ok = false; if (bytes) *bytes = 0; if (img) { if(header) img->copy_header(header); if(bytes) *bytes = img->bytes(); if (dpi) *dpi = img->dpi(); ok = true; img->release(); } return ok; } void safe_img_queue::clear() { safe_queue::clear(&safe_img_queue::free_image); }