增加内存映射文件,以支持在小内存工况下正常工作

This commit is contained in:
gb 2022-06-16 16:04:58 +08:00
parent 8da59821bd
commit e579975168
6 changed files with 527 additions and 61 deletions

View File

@ -131,7 +131,9 @@ __declspec(novtable) struct IScanImg : public IRef
COM_API_DECLARE(int, channel(void));
COM_API_DECLARE(SANE_Frame, type(void));
COM_API_DECLARE(unsigned int, bytes(void));
COM_API_DECLARE(unsigned char*, bits(void));
COM_API_DECLARE(unsigned char*, data(unsigned long long off, unsigned int *bytes));
COM_API_DECLARE(const char*, file(void));
COM_API_DECLARE(void, keep_file(bool keep));
COM_API_DECLARE(void, copy_header(SANE_Parameters* head));
};
__declspec(novtable) struct ISaneInvoker : public IRef

View File

@ -1,9 +1,58 @@
#include "scanned_img.h"
#include <Windows.h>
#include <Shlwapi.h>
#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)
@ -27,44 +76,328 @@ COM_API_IMPLEMENT(refer, long, release(void))
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// class scanned_img
scanned_img::scanned_img(SANE_Parameters head, unsigned char* data) : head_(head)
{
size_t bytes = line_bytes() * height();
std::string h(file_header(SANE_IMAGE_TYPE_BMP, 200.0f));
unsigned char* src = data + head.bytes_per_line * head.lines - head.bytes_per_line,
* dst = NULL;
// class mapping_buf
const unsigned int max_mem_block = 2 * 1024 * 1024;
bytes_ = bytes + h.length();
data_ = new unsigned char[bytes_];
memcpy(data_, h.c_str(), h.length());
dst = data_ + h.length();
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 };
if (head.format == SANE_FRAME_RGB)
{
for (int i = 0; i < height(); ++i)
{
for (int j = 0; j < head.pixels_per_line; ++j)
{
dst[j * 3 + 0] = src[j * 3 + 2];
dst[j * 3 + 1] = src[j * 3 + 1];
dst[j * 3 + 2] = src[j * 3 + 0];
GetSystemInfo(&si);
page_size_ = si.dwPageSize;
map_unit_ = si.dwAllocationGranularity;
}
src -= head.bytes_per_line;
dst += line_bytes();
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
{
for (int i = 0; i < height(); ++i, dst += line_bytes(), src -= head.bytes_per_line)
memcpy(dst, src, head.bytes_per_line);
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;
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, data, len);
total += len;
off += len;
src += len;
len = *bytes - total;
buf = 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_ ? NULL : 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) : head_(head), dpi_(dpi)
{
size_t bytes = line_bytes() * height();
std::string h(file_header(SANE_IMAGE_TYPE_BMP, dpi));
unsigned char* dst = NULL;
bool ok = false;
data_ = new mapping_buf();
dst = data_->allocate(tmp_file, bytes + h.length());
if (dst)
{
unsigned long long off = 0;
bytes = h.length();
if (data_->save(h.c_str(), &bytes, off))
{
unsigned int line = line_bytes(), len = line;
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);
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;
rcv = 0;
dst = data_->buffer(off, &len);
}
else
{
len = want_to_read;
dst = data_->buffer(off + rcv, &len);
}
}
}
ok = off == h.length();
}
if (ok)
{
if (channel() == 3)
{
// swap RGB
unsigned long long off = 0;
unsigned int line = line_bytes(), len = line;
for (int i = 0; i < height(); ++i)
{
int l = head_.bytes_per_line, cur = 0;
off = i * line + h.length();
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;
}
}
data_->unmap();
}
else
{
delete data_;
data_ = NULL;
}
}
scanned_img::~scanned_img()
{
if (data_)
delete[] data_;
delete data_;
}
std::string scanned_img::file_header(SANE_ImageType type, float resolution)
@ -131,11 +464,23 @@ COM_API_IMPLEMENT(scanned_img, SANE_Frame, type(void))
}
COM_API_IMPLEMENT(scanned_img, unsigned int, bytes(void))
{
return bytes_;
return data_ ? data_->bytes() : 0;
}
COM_API_IMPLEMENT(scanned_img, unsigned char*, bits(void))
COM_API_IMPLEMENT(scanned_img, unsigned char*, data(unsigned long long off, unsigned int* bytes))
{
return data_;
return data_ ? data_->buffer(off, bytes) : NULL;
}
COM_API_IMPLEMENT(scanned_img, const char*, file(void))
{
if (data_)
return data_->file();
else
return NULL;
}
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))
{

View File

@ -16,16 +16,49 @@ public:
COM_API_OVERRIDE(long, release(void));
};
class mapping_buf
{
unsigned long long bytes_;
unsigned long long offset_;
unsigned int page_size_;
unsigned int map_unit_;
unsigned int mapped_bytes_;
HANDLE map_;
bool is_mem_;
unsigned char* buf_;
std::string file_;
bool rmv_file_;
void init_map(const char* file, unsigned long long size);
void close(void);
void map(void);
void set_buffer(unsigned char*& buf, unsigned long long off, unsigned int* bytes);
public:
mapping_buf();
~mapping_buf();
public:
unsigned char* allocate(const wchar_t* file, unsigned long long size = 0);
unsigned char* buffer(unsigned long long off, unsigned int* bytes);
bool save(const void* data, size_t* bytes, unsigned long long off);
void unmap();
void set_remove_file_when_destroyed(bool rmv);
const char* file(void);
unsigned long long bytes(void);
unsigned long long offset(void);
unsigned int mapped_bytes(void);
};
class scanned_img : public IScanImg, virtual public refer
{
SANE_Parameters head_;
unsigned char* data_;
unsigned int bytes_;
mapping_buf* data_;
int dpi_;
std::string file_header(SANE_ImageType type, float resolution);
public:
scanned_img(SANE_Parameters head, unsigned char* data);
scanned_img(SANE_Parameters head, SANE_Handle dev, int dpi, const wchar_t* tmp_file);
protected:
@ -45,7 +78,14 @@ public:
COM_API_OVERRIDE(int, channel(void));
COM_API_OVERRIDE(SANE_Frame, type(void));
COM_API_OVERRIDE(unsigned int, bytes(void));
COM_API_OVERRIDE(unsigned char*, bits(void));
COM_API_OVERRIDE(unsigned char*, data(unsigned long long off, unsigned int* bytes));
COM_API_OVERRIDE(const char*, file(void));
COM_API_OVERRIDE(void, keep_file(bool keep));
COM_API_OVERRIDE(void, copy_header(SANE_Parameters* head));
};
namespace local_trans
{
std::string u2a(const wchar_t* unic, UINT cp = CP_ACP);
std::wstring a2u(const char* asc, UINT cp = CP_ACP);
}

View File

@ -21,7 +21,13 @@ static IMPLEMENT_OPTION_STRING_COMPARE(compare_sane_opt);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// class scanner
scanner::scanner(SCANNERID id) : handle_(NULL), id_(id), ex_id_(EXTENSION_ID_BASE), prev_start_result_(SCANNER_ERR_NOT_START)
, dpi_(200), tmp_path_(L""), img_ind_(0)
{
tmp_path_ = local_trans::a2u(hg_sane_middleware::sane_path().c_str());
tmp_path_ += L"imgs";
CreateDirectoryW(tmp_path_.c_str(), NULL);
tmp_path_ += L"\\";
err_ = open();
}
scanner::~scanner()
@ -419,32 +425,50 @@ int scanner::set_option_value(int sn, SANE_Value_Type type, int size, void* data
SANE_Int si = 0, after = 0;
SANE_Fixed sf = 0;
int ret = SCANNER_ERR_OK;
void* val = data;
if (type == SANE_TYPE_BOOL)
{
sb = *(bool*)data ? SANE_TRUE : SANE_FALSE;
data = &sb;
val = &sb;
}
else if (type == SANE_TYPE_INT)
{
si = *(int*)data;
data = &si;
val = &si;
}
else if (type == SANE_TYPE_FIXED)
{
sf = SANE_FIX(*(float*)data);
data = &sf;
val = &sf;
}
else
{
buf = new char[size + 4];
memset(buf, 0, size + 4);
strcpy(buf, ((std::string*)data)->c_str());
data = buf;
val = buf;
}
ret = hg_sane_middleware::instance()->set_option(handle_, sn, SANE_ACTION_SET_VALUE, data, &after);
if (buf)
ret = hg_sane_middleware::instance()->set_option(handle_, sn, SANE_ACTION_SET_VALUE, val, &after);
if (type == SANE_TYPE_BOOL)
{
*(bool*)data = sb == SANE_TRUE;
}
else if (type == SANE_TYPE_INT)
{
*(int*)data = si;
}
else if (type == SANE_TYPE_FIXED)
{
*(float*)data = SANE_UNFIX(sf);
}
else if(buf)
{
strcpy((char*)val, buf);
delete[] buf;
}
return ret;
}
@ -1345,7 +1369,7 @@ COM_API_IMPLEMENT(scanner, int, start(void))
prev_start_result_ = ret;
return ret;
return local_utility::sane_statu_2_scanner_err(ret);
}
COM_API_IMPLEMENT(scanner, int, stop(void))
{
@ -1392,19 +1416,9 @@ COM_API_IMPLEMENT(scanner, IScanImg*, take_first_image(void))
if (hg_sane_middleware::instance()->get_image_parameters(handle_, &head) == SANE_STATUS_GOOD)
{
int off = 0, size = head.bytes_per_line * head.lines, rcv = size;
unsigned char* buf = new unsigned char[size];
while (hg_sane_middleware::instance()->read(handle_, buf + off, &rcv) == SANE_STATUS_GOOD)
{
off += rcv;
if (off >= size)
break;
rcv = size - off;
}
img = new scanned_img(head, buf);
delete[] buf;
wchar_t name[40] = { 0 };
swprintf_s(name, _countof(name) - 1, L"img_%05u.bmp", ++img_ind_);
img = new scanned_img(head, handle_, dpi_, (tmp_path_ + name).c_str());
}
return dynamic_cast<IScanImg*>(img);
@ -1476,6 +1490,8 @@ COM_API_IMPLEMENT(scanner, bool, get_value(int sn, set_opt_value setval, void* p
hg_sane_middleware::instance()->get_cur_value(handle_, sn, &cur);
val = cur;
if (sn == resolution_id_)
dpi_ = cur;
do
{
if (desc->constraint_type == SANE_CONSTRAINT_RANGE)
@ -1508,6 +1524,8 @@ COM_API_IMPLEMENT(scanner, bool, get_value(int sn, set_opt_value setval, void* p
float val = .0f;
hg_sane_middleware::instance()->get_cur_value(handle_, sn, &cur);
if (sn == resolution_id_)
dpi_ = SANE_UNFIX(cur) + .5f;
do
{
if (desc->constraint_type == SANE_CONSTRAINT_RANGE)
@ -1594,6 +1612,14 @@ COM_API_IMPLEMENT(scanner, int, set_value(int sn, void* val))
ret = (this->*ex->ex_api)(ex->base_ind, val, NULL);
}
if (sn == resolution_id_)
{
if (desc->type == SANE_TYPE_FIXED)
dpi_ = *(float*)val + .5f;
else
dpi_ = *(int*)val;
}
return ret;
}

View File

@ -28,6 +28,9 @@ class scanner : public ISaneInvoker, virtual public refer
int err_;
int ex_id_;
int prev_start_result_;
int dpi_;
unsigned int img_ind_;
std::wstring tmp_path_;
int open(void);
int close(void);

View File

@ -926,7 +926,9 @@ Result huagao_ds::imageMemXferGet(const Identity& origin, ImageMemXfer& data)
return seqError();
IScanImg *img = scanner_->take_first_image();
unsigned char *src = img->bits() + img->bytes() - img->line_bytes(),
unsigned long long off = 0;
unsigned int total = img->bytes();
unsigned char *src = img->data(off, &total),
*dst = NULL;
data.setBytesPerRow(img->line_bytes());
@ -937,8 +939,16 @@ Result huagao_ds::imageMemXferGet(const Identity& origin, ImageMemXfer& data)
data.setYOffset(0);
data.setCompression(Compression::None);
dst = (unsigned char*)data.memory().data().data();
for (int i = 0; i < img->height(); ++i, src -= img->line_bytes(), dst += img->line_bytes())
std::copy(src, src + img->line_bytes(), dst);
while (off < img->bytes() && src)
{
std::copy(src, src + total, dst);
dst += total;
off += total;
if (off >= img->bytes())
break;
total = img->bytes() - off;
src = img->data(off, &total);
}
img->release();
return success();
@ -955,7 +965,21 @@ Result huagao_ds::imageNativeXferGet(const Identity& id, ImageNativeXfer& data)
if (data)
data.release();
data = ImageNativeXfer(img->bytes());
std::copy(img->bits(), img->bits() + img->bytes(), data.data<unsigned char>().data());
unsigned long long off = 0;
unsigned int total = img->bytes();
unsigned char* src = img->data(off, &total),
* dst = data.data<unsigned char>().data();
while (off < img->bytes() && src)
{
std::copy(src, src + total, dst);
dst += total;
off += total;
if (off >= img->bytes())
break;
total = img->bytes() - off;
src = img->data(off, &total);
}
img->release();
return { ReturnCode::XferDone, ConditionCode::Success };
@ -977,19 +1001,45 @@ Twpp::Result huagao_ds::imageFileXferGet(const Twpp::Identity& origin)
FILE* dst = NULL;
if (img)
{
std::string file(img->file());
if (file.empty())
{
dst = fopen(m_fileXfer.filePath().string().c_str(), "wb");
ret = { ReturnCode::Failure, ConditionCode::FileWriteError };
if (dst)
{
if(fwrite(img->bits(), 1, img->bytes(), dst) == img->bytes())
unsigned long long off = 0;
unsigned int total = img->bytes();
unsigned char* src = img->data(off, &total);
while (src)
{
if (fwrite(src, 1, total, dst) != total)
break;
off += total;
if (off >= img->bytes())
{
ret = Result(ReturnCode::XferDone, ConditionCode::Success);
break;
}
total = img->bytes() - off;
src = img->data(off, &total);
}
fclose(dst);
}
img->release();
}
else
{
img->keep_file(true);
img->release();
if (MoveFileA(file.c_str(), m_fileXfer.filePath().string().c_str()))
ret = Result(ReturnCode::XferDone, ConditionCode::Success);
else
DeleteFileA(file.c_str());
}
}
return ret;
}