#include "scanned_img.h" #include #include #pragma comment(lib, "Shlwapi.lib") #include "../../code_device/hgsane/sane_hg_mdw.h" ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // namespace local_trans { std::string u2a(const wchar_t* u, UINT cp) { std::string a(""); if (u) { char * ansi = NULL; int len = 0; len = WideCharToMultiByte(cp, 0, u, lstrlenW(u), NULL, 0, NULL, NULL); ansi = new char[len + 2]; len = WideCharToMultiByte(cp, 0, u, lstrlenW(u), ansi, len, NULL, NULL); ansi[len--] = 0; a = ansi; delete[] ansi; } return a; } std::wstring a2u(const char* asc, UINT cp) { std::wstring u(L""); if (asc) { wchar_t *buf = NULL; int len = 0; len = MultiByteToWideChar(cp, 0, asc, lstrlenA(asc), NULL, 0); buf = new wchar_t[len + 2]; len = MultiByteToWideChar(cp, 0, asc, lstrlenA(asc), buf, len); buf[len--] = 0; u = buf; delete[] buf; } return u; } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // class refer refer::refer() : ref_(1) {} refer::~refer() {} COM_API_IMPLEMENT(refer, long, add_ref(void)) { return InterlockedIncrement(&ref_); } COM_API_IMPLEMENT(refer, long, release(void)) { long ref = InterlockedDecrement(&ref_); if (ref == 0) delete this; return ref; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // class mapping_buf const unsigned int max_mem_block = 2 * 1024 * 1024; mapping_buf::mapping_buf() : bytes_(0), offset_(0), mapped_bytes_(0), map_(NULL), buf_(NULL), file_(""), rmv_file_(true), page_size_(0), is_mem_(false) { SYSTEM_INFO si = { 0 }; GetSystemInfo(&si); page_size_ = si.dwPageSize; map_unit_ = si.dwAllocationGranularity; } mapping_buf::~mapping_buf() { close(); } void mapping_buf::init_map(const char* file, unsigned long long size) { HANDLE f = INVALID_HANDLE_VALUE; if (size) { f = CreateFileA(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (f != INVALID_HANDLE_VALUE) { unsigned long long total = size - 1; LONG* p32 = (LONG*)&total; *p32 = SetFilePointer(f, *p32, p32 + 1, FILE_BEGIN); total++; if (total == size) { DWORD wrote = 1; WriteFile(f, "\0", 1, &wrote, NULL); map_ = CreateFileMapping(f, NULL, PAGE_READWRITE, p32[1], p32[0], NULL); } CloseHandle(f); if (!map_) DeleteFileA(file); } } else { f = CreateFileA(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (f != INVALID_HANDLE_VALUE) { DWORD* p32 = (DWORD*)&size; *p32 = GetFileSize(f, p32 + 1); map_ = CreateFileMapping(f, NULL, PAGE_READWRITE, p32[1], p32[0], NULL); CloseHandle(f); } } if (map_) { bytes_ = size; file_ = file; buffer(0, NULL); if (!map_) close(); } } void mapping_buf::close(void) { if (buf_) { if (is_mem_) delete[] buf_; else UnmapViewOfFile(buf_); } buf_ = NULL; if (map_) CloseHandle(map_); map_ = NULL; if (rmv_file_ && file_.length()) DeleteFileA(file_.c_str()); file_ = ""; bytes_ = offset_ = 0; mapped_bytes_ = 0; rmv_file_ = true; is_mem_ = false; } void mapping_buf::map(void) { DWORD* off = (DWORD*)&offset_; buf_ = (unsigned char*)MapViewOfFile(map_, FILE_MAP_READ | FILE_MAP_WRITE, off[1], off[0], mapped_bytes_); if (!buf_) { DWORD err = GetLastError(); mapped_bytes_ /= map_unit_; mapped_bytes_ *= map_unit_; while (mapped_bytes_ > map_unit_) { buf_ = (unsigned char*)MapViewOfFile(map_, FILE_MAP_READ | FILE_MAP_WRITE, off[1], off[0], mapped_bytes_); if (buf_) break; mapped_bytes_ /= 2; } if (!buf_) mapped_bytes_ = 0; } } void mapping_buf::set_buffer(unsigned char*& buf, unsigned long long off, unsigned int* bytes) { buf = buf_ + (off - offset_); if (bytes) *bytes = mapped_bytes_ - (off - offset_); } unsigned char* mapping_buf::allocate(const wchar_t* file, unsigned long long size) { close(); std::string ansi(local_trans::u2a(file)); if (size >= 100 * 1024 * 1024 || PathFileExistsW(file)) { init_map(ansi.c_str(), size); } else { try { buf_ = new unsigned char[size]; is_mem_ = true; bytes_ = mapped_bytes_ = size; } catch (...) { is_mem_ = false; init_map(ansi.c_str(), size); } } return buf_; } unsigned char* mapping_buf::buffer(unsigned long long off, unsigned int* bytes) { unsigned int size = bytes ? *bytes : 1 * 1024 * 1024 * 1024; unsigned char* buf = NULL; if (size > bytes_ - offset_) size = bytes_ - offset_; if (buf_ && off >= offset_ && size + (off - offset_) <= mapped_bytes_) { set_buffer(buf, off, bytes); } else if (!is_mem_) { if (off < bytes_) { if (buf_) UnmapViewOfFile(buf_); offset_ = off / map_unit_ * map_unit_; mapped_bytes_ = bytes_ - offset_; map(); if (buf_) set_buffer(buf, off, bytes); } } return buf; } bool mapping_buf::save(const void* data, size_t* bytes, unsigned long long off) { unsigned int len = *bytes, total = 0; unsigned char* buf = buffer(off, &len); bool ret = false; const char* src = (const char*)data; while (buf) { if (len >= *bytes - total) { memcpy(buf, src, *bytes - total); total = *bytes; ret = true; break; } memcpy(buf, src, len); total += len; off += len; src += len; len = *bytes - total; buf = buffer(off, &len); } *bytes = total; return ret; } int mapping_buf::read(void* buf, size_t* bytes, unsigned long long off) { if (!bytes) return SCANNER_ERR_INVALID_PARAMETER; unsigned int len = *bytes, total = 0; unsigned char *src = buffer(off, &len), *dst = (unsigned char*)buf; int ret = SCANNER_ERR_OUT_OF_RANGE; while (src) { if (len >= *bytes - total) { memcpy(dst, src, *bytes - total); total = *bytes; ret = SCANNER_ERR_OK; break; } memcpy(dst, src, len); total += len; off += len; len = *bytes - total; src = buffer(off, &len); } *bytes = total; return ret; } void mapping_buf::unmap() { if (!is_mem_) { if (buf_) UnmapViewOfFile(buf_); buf_ = NULL; offset_ = 0; mapped_bytes_ = 0; } } void mapping_buf::set_remove_file_when_destroyed(bool rmv) { rmv_file_ = rmv; } const char* mapping_buf::file(void) { return is_mem_ ? "" : file_.c_str(); } unsigned long long mapping_buf::bytes(void) { return bytes_; } unsigned long long mapping_buf::offset(void) { return offset_; } unsigned int mapping_buf::mapped_bytes(void) { return mapped_bytes_; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // class scanned_img scanned_img::scanned_img(SANE_Parameters head, SANE_Handle dev, int dpi , const wchar_t* tmp_file, twain_xfer xfer , SANE_FinalImgFormat *fmt) : head_(head), dpi_(dpi), header_size_(0) { 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, dpi, xfer)); unsigned char* dst = NULL; bool ok = false; data_ = new mapping_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 > len - rcv ? len - rcv : want_to_read; int ret = hg_sane_middleware::instance()->read(dev, dst + rcv, &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_Parameters head, void* data, unsigned int len, int dpi, const wchar_t* tmp_file , twain_xfer xfer, SANE_FinalImgFormat* fmt) : head_(head), dpi_(dpi), header_size_(0) { 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, dpi, xfer)); unsigned char* dst = NULL, *src = (unsigned char*)data; bool ok = false; header_size_ = h.length(); data_ = new mapping_buf(); bytes += header_size_; dst = data_->allocate(tmp_file, bytes); bytes = h.length(); if (dst && 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_->bytes() - line_l; for (; row < 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_; } 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 }; 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 = resolution * 39.37f + .5f; if (xfer == TWAIN_XFER_File) { BITMAPFILEHEADER fh = { 0 }; fh.bfType = MAKEWORD('B', 'M'); fh.bfSize = sizeof(fh) + bih.biSizeImage + sizeof(bih); fh.bfOffBits = sizeof(fh) + sizeof(bih); h = std::string((char*)&fh, sizeof(fh)); } h += std::string((char*)&bih, sizeof(bih)); } return h; } void scanned_img::do_result(bool ok, twain_xfer xfer) { if (ok) { if (fmt_.img_format == SANE_IMAGE_TYPE_BMP && channel() == 3 && xfer != TWAIN_XFER_Memory) { // swap RGB swap_rgb(); } data_->unmap(); } 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 > l) len = l; len /= 3; for (int pos = 0; pos < 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) 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, SANE_Frame, type(void)) { return head_.format; } COM_API_IMPLEMENT(scanned_img, unsigned int, bytes(void)) { return data_ ? 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)) { if (data_) return data_->file(); else return ""; } COM_API_IMPLEMENT(scanned_img, void, keep_file(bool keep)) { if (data_) data_->set_remove_file_when_destroyed(!keep); } COM_API_IMPLEMENT(scanned_img, void, copy_header(SANE_Parameters* head)) { *head = head_; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // class safe_img_queue safe_img_queue::safe_img_queue() {} safe_img_queue::~safe_img_queue() { clear(); } size_t safe_img_queue::count(void) { std::lock_guard lock(que_lock_); return queue_.size(); } bool safe_img_queue::save(scanned_img* img) { std::lock_guard lock(que_lock_); queue_.push_back(img); return true; } bool safe_img_queue::get_header(SANE_Parameters* header) { std::lock_guard lock(que_lock_); bool ok = false; if (queue_.size()) { queue_[0]->copy_header(header); ok = true; } return ok; } scanned_img* safe_img_queue::take(void) { std::lock_guard lock(que_lock_); scanned_img* img = NULL; if (queue_.size()) { img = queue_[0]; queue_.erase(queue_.begin()); } return img; } void safe_img_queue::clear() { std::lock_guard lock(que_lock_); for (size_t i = 0; i < queue_.size(); ++i) queue_[i]->release(); queue_.clear(); }