From 9962ac5d7d6e15f7de14c6bb583a53eb2fa29352 Mon Sep 17 00:00:00 2001 From: gb <741021719@qq.com> Date: Thu, 14 Jul 2022 14:21:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0G402=E5=9B=BE=E5=83=8F?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hgdriver/hgdev/hg_scanner_402.cpp | 353 ++++++++++++++++++++++-------- hgdriver/hgdev/image_process.cpp | 80 ++++++- hgdriver/hgdev/image_process.h | 1 + 3 files changed, 343 insertions(+), 91 deletions(-) diff --git a/hgdriver/hgdev/hg_scanner_402.cpp b/hgdriver/hgdev/hg_scanner_402.cpp index d38bd2c..b73af85 100644 --- a/hgdriver/hgdev/hg_scanner_402.cpp +++ b/hgdriver/hgdev/hg_scanner_402.cpp @@ -460,7 +460,11 @@ std::string hg_scanner_402::control_fetch(int addr, int val, int size) data.resize(size + 2); bzero(&data[0], size + 2); ret = io_->read_bulk(&data[0], &l); - if (ret) + if (ret == SCANNER_ERR_OK) + { + VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "control_fetch(%d, %d) - read_bulk %d bytes\n", addr, val, l); + } + else { VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "control_fetch(%d, %d) - read_bulk = %s\n", addr, val, hg_scanner_err_name(ret)); data.clear(); @@ -669,6 +673,159 @@ void hg_scanner_402::image_process(std::shared_ptr& buff) err = hg_imgproc::load_buffer(handle, buff); err = hg_imgproc::decode(handle,pid_); + VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "Image process parameter: 0x%x%08x, multi out: %s\n", ((unsigned int*)&image_prc_param_.value)[1], ((unsigned int*)&image_prc_param_.value)[0], is_multiout ? "true" : "false"); + if (img_conf_.fillhole.is_fillhole) + { + err = hg_imgproc::fillhole(handle); + //printf("填穿孔开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "填穿孔开启:%s\n",hg_scanner_err_name(ret)); + } + + /////////////////////////////////////111111111111111111111111111////////////////////////////////// + err = hg_imgproc::auto_crop(handle); + + if (img_conf_.is_autodiscradblank_normal || img_conf_.is_autodiscradblank_vince) + { + err = hg_imgproc::discardBlank(handle); + printf("丢弃空白页开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "丢弃空白页开启:%s\n",hg_scanner_err_name(ret)); + } + if (img_conf_.fadeback) + { + hg_imgproc::fadeback(handle, img_conf_.fadebackrange, param.double_side); + //printf("背景除色开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "背景除色开启:%s\n",hg_scanner_err_name(ret)); + } + if (img_conf_.resolution_dst != img_conf_.resolution_native) + { + hg_imgproc::resolution_change(handle); + //printf("dpi改变开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "dpi改变开启:%s\n",hg_scanner_err_name(ret)); + } + if (img_conf_.cropRect.enable && !img_conf_.is_autocrop) + { + hg_imgproc::croprect(handle); + //printf("手动裁图开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "手动裁图开启:%s\n",hg_scanner_err_name(ret)); + } + if (img_conf_.filter != ColorFilter::FILTER_NONE && (img_conf_.pixtype == COLOR_MODE_BLACK_WHITE || img_conf_.pixtype == COLOR_MODE_256_GRAY)) + { + printf("除色开启\r\n"); + hg_imgproc::channel(handle); + + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "除色开启:%s\n",hg_scanner_err_name(ret)); + } + /////////////////////////////////////2222222222222222222222222222222222222////////////////////////////////// + int tableLength;//= sizeof(custom_gamma_val_->table)/sizeof(custom_gamma_val_->table[0]); + + unsigned char buffer1[256 * 3]; + if (img_conf_.pixtype == COLOR_MODE_BLACK_WHITE || img_conf_.pixtype == COLOR_MODE_256_GRAY) + tableLength = 256; + else + { + + tableLength = 768; + int index = 0; + const int buffersize = 256; + + unsigned char buf_01[buffersize]; + memcpy(buf_01, custom_gamma_val_->table + index, buffersize); + index += buffersize; + + unsigned char buf_02[buffersize]; + memcpy(buf_02, custom_gamma_val_->table + index, buffersize); + index += buffersize; + + unsigned char buf_03[buffersize]; + memcpy(buf_03, custom_gamma_val_->table + index, buffersize); + index += buffersize; + + int j = 0; + for (size_t i = 0; i < buffersize; i++) + { + memcpy(buffer1 + j, buf_01 + i, 1); + memcpy(buffer1 + (++j), buf_02 + i, 1); + memcpy(buffer1 + (++j), buf_03 + i, 1); + ++j; + } + } + + + hg_imgproc::customgamma(handle, custom_gamma_, buffer1, tableLength); + + if (img_conf_.pixtype == COLOR_MODE_24_BITS && img_conf_.hsvcorrect) + { + hg_imgproc::answerSheetFilterRed(handle); + //printf("答题卡出红开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "答题卡出红开启:%s\n",hg_scanner_err_name(ret)); + } + if (img_conf_.refuseInflow) + { + int lv = 5; + + if (image_prc_param_.bits.is_permeate_lv_ == 0) + lv = 5; + else if (image_prc_param_.bits.is_permeate_lv_ == 1) + lv = 15; + else if (image_prc_param_.bits.is_permeate_lv_ == 2) + lv = 25; + else if (image_prc_param_.bits.is_permeate_lv_ == 3) + lv = 35; + else if (image_prc_param_.bits.is_permeate_lv_ == 4) + lv = 45; + + hg_imgproc::antiInflow(handle, lv); + } + if (img_conf_.colorCorrection && img_conf_.pixtype != COLOR_MODE_BLACK_WHITE) + { + hg_imgproc::colorCorrection(handle); + //printf("颜色校正开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "颜色校正开启:%s\n",hg_scanner_err_name(ret)); + } + if ((img_conf_.imageRotateDegree != TEXT_DIRECTION_0 || img_conf_.is_backrotate180) && (!img_conf_.is_autotext)) + { + printf("手动旋转开启\r\n"); + hg_imgproc::orentation(handle); + } + if (img_conf_.removeMorr) + { + hg_imgproc::textureRemove(handle); + //printf("除摩尔纹开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "除摩尔纹开启:%s\n",hg_scanner_err_name(ret)); + } + if (img_conf_.detachnoise.is_detachnoise) + { + hg_imgproc::nosieDetach(handle); + //printf("噪点优化开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "噪点优化开启:%s\n",hg_scanner_err_name(ret)); + } + if (img_conf_.pixtype == COLOR_MODE_BLACK_WHITE) + { + hg_imgproc::errorextention(handle); + //printf("黑白图处理开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "黑白图处理开启:%s\n",hg_scanner_err_name(ret)); + } + if (img_conf_.en_fold) + { + hg_imgproc::fold(handle); + //printf("对折开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "对折开启:%s\n",hg_scanner_err_name(ret)); + } + + /////////////////////////////////// + + if (image_prc_param_.bits.rid_red && img_conf_.pixtype == COLOR_MODE_24_BITS) + { + err = hg_imgproc::multi_out_red(handle); + //printf("多流出红开启\r\n"); + //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "多流出红开启:%s\n",hg_scanner_err_name(ret)); + } + + if (img_conf_.sharpen) + { + hg_imgproc::sharpenType(handle); + } + if(image_prc_param_.bits.erase_bakground && !user_cancel_) { err = hg_imgproc::fadeback(handle,img_conf_.fadebackrange,img_conf_.is_duplex); @@ -781,58 +938,59 @@ int hg_scanner_402::writedown_device_configuration(HGSCANCONF *dev_conf) int hg_scanner_402::writedown_image_configuration(void) { - int ret = write_register(SR_CONFIF_IMGPROCPARAM, sizeof(SCANCONF)); - if (ret != SCANNER_ERR_OK) - return ret; + //int ret = write_register(SR_CONFIF_IMGPROCPARAM, sizeof(SCANCONF)); + //if (ret != SCANNER_ERR_OK) + // return ret; + int ret = SCANNER_ERR_OK; SCANCONF ic; - int len = sizeof(ic); + int len = sizeof(ic); bzero(&ic, len); ic.papertype = paper_size_; -if (is_lateral(image_prc_param_.bits.paper)) - ic.paperAlign = Rot270; -// else if (image_prc_param_.bits.text_direction == TEXT_DIRECTION_AUTO) -// ic.paperAlign = AutoTextOrientation; -else - ic.paperAlign = Rot0; + if (is_lateral(image_prc_param_.bits.paper)) + ic.paperAlign = Rot270; + // else if (image_prc_param_.bits.text_direction == TEXT_DIRECTION_AUTO) + // ic.paperAlign = AutoTextOrientation; + else + ic.paperAlign = Rot0; - ic.en_sizecheck = dev_conf_.g200params.enable_sizecheck; + ic.en_sizecheck = dev_conf_.g200params.enable_sizecheck; -if (image_prc_param_.bits.text_direction != TEXT_DIRECTION_AUTO) - ic.imageRotateDegree = (float)image_prc_param_.bits.text_direction; -else - ic.imageRotateDegree = 0; + if (image_prc_param_.bits.text_direction != TEXT_DIRECTION_AUTO) + ic.imageRotateDegree = (float)image_prc_param_.bits.text_direction; + else + ic.imageRotateDegree = 0; - ic.imageRotateDegree *= 90.0f; + ic.imageRotateDegree *= 90.0f; - ic.is_duplex = (image_prc_param_.bits.page == PAGE_DOUBLE - || image_prc_param_.bits.page == PAGE_OMIT_EMPTY - || image_prc_param_.bits.page == PAGE_OMIT_EMPTY_RECEIPT - || image_prc_param_.bits.page ==PAGE_FOLIO); + ic.is_duplex = (image_prc_param_.bits.page == PAGE_DOUBLE + || image_prc_param_.bits.page == PAGE_OMIT_EMPTY + || image_prc_param_.bits.page == PAGE_OMIT_EMPTY_RECEIPT + || image_prc_param_.bits.page ==PAGE_FOLIO); - ic.en_fold = (image_prc_param_.bits.page == PAGE_FOLIO); - ic.pixtype = image_prc_param_.bits.color_mode == COLOR_MODE_AUTO_MATCH ? 2 : image_prc_param_.bits.color_mode; - ic.automaticcolor = is_auto_matic_color; + ic.en_fold = (image_prc_param_.bits.page == PAGE_FOLIO); + ic.pixtype = image_prc_param_.bits.color_mode == COLOR_MODE_AUTO_MATCH ? 2 : image_prc_param_.bits.color_mode; + ic.automaticcolor = is_auto_matic_color; - ic.automaticcolortype = 1;// ic.pixtype; //存疑 -if (resolution_ >= 300) -{ - if(is_quality_ == IMG_SPEED) + ic.automaticcolortype = 1;// ic.pixtype; //存疑 + if (resolution_ >= 300) { - ic.resolution_dst = 200; + if(is_quality_ == IMG_SPEED) + { + ic.resolution_dst = 200; + } + else if(is_quality_ ==IMG_QUALITY) + { + ic.resolution_dst = resolution_; + } } - else if(is_quality_ ==IMG_QUALITY) + else { ic.resolution_dst = resolution_; } -} -else -{ - ic.resolution_dst = resolution_; -} ic.resolution_native = 200.0f; ic.gamma = (float)gamma_; @@ -956,14 +1114,14 @@ else ic.cropRect.enable = false; } - { - std::lock_guard lock(io_lock_); - - ret = io_->write_bulk(&ic, &len); - - this_thread::sleep_for(chrono::milliseconds(500)); - io_->set_timeout(2000);//必要延时 - } + //{ + // std::lock_guard lock(io_lock_); + // + // ret = io_->write_bulk(&ic, &len); + // + // this_thread::sleep_for(chrono::milliseconds(500)); + // io_->set_timeout(2000);//必要延时 + //} VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "Write-down 0x%x bytes image process parameters\n", len); VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO,"\n ic.pixtype=%d", ic.pixtype); VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO,"\n ic.papertype=%d", ic.papertype); @@ -1037,6 +1195,7 @@ int hg_scanner_402::read_one_image_from_usb(void) off = 0, ret = SCANNER_ERR_OK; + VLOG_MINI_1(LOG_LEVEL_ALL, "New image size: %u\n", total); if (!waiting_for_memory_enough(total)) { status_ = SCANNER_ERR_INSUFFICIENT_MEMORY; @@ -1063,63 +1222,45 @@ int hg_scanner_402::read_one_image_from_usb(void) r = total; { std::lock_guard lock(io_lock_); + void* buff = buf->data(off, (unsigned int*)&r); - unsigned int size = r; - void* buff = buf->data(off, &size); - int block = 0; - if (!buff) + ret = SCANNER_ERR_INSUFFICIENT_MEMORY; + while (buff) { - VLOG_MINI_3(LOG_LEVEL_FATAL, "memory(0x%08x + %u) fatal when read USB image %d !!!\n", off, r, usb_img_index_); - ret = SCANNER_ERR_INSUFFICIENT_MEMORY; - } - else - { - r = size; - while (r > 0) - { - block = 512 * 1024; - - if (r < block) - block = r; + ret = io_->read_bulk(buff, &r); + if (ret != SCANNER_ERR_OK) + break; - unsigned int size = block; - void* buff = buf->data(off, &size); - if (!buf) - { - //VLOG_MINI_3(LOG_LEVEL_FATAL, "memory(0x%08x + %u) fatal when read USB image %d !!!\n", index, block, usb_img_index_); - ret = SCANNER_ERR_INSUFFICIENT_MEMORY; - break; - } - block = size; - ret = io_->read_bulk(buff, &block); - if (ret != SCANNER_ERR_OK) - break; + off += r; + if (off >= total) + break; - off += block; - r -= block; - } + r = total - off; + buff = buf->data(off, (unsigned int*)&r); } } + VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "Read image from USB = %s\n", hg_scanner_err_name(ret)); if (ret == SCANNER_ERR_OK) { - VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "Read image from USB = %s\n", hg_scanner_err_name(ret)); ret = save_usb_data(buf); if (ret == SCANNER_ERR_OK) { pop_first_image(); - //if(image_prc_param_.bits.page == PAGE_SINGLE) - // pop_first_image(); + } + else + { + VLOG_MINI_1(LOG_LEVEL_FATAL, "save usb data failed: %s\n", hg_scanner_err_name(ret)); } } else { - char msg[128]; - sprintf(msg, "Read image data from USB err: %s\n", hg_scanner_err_name(ret)); - LOG_INFO(LOG_LEVEL_DEBUG_INFO, msg); - notify_ui_working_status(msg); - notify_ui_working_status(STATU_DESC_SCANNER_ERR_TIMEOUT, SANE_EVENT_ERROR); + VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "Read image data from USB err: %s\n", hg_scanner_err_name(ret)); } } + else + { + VLOG_MINI_1(LOG_LEVEL_FATAL, "write command SR_IM_TX failed: %s\n", hg_scanner_err_name(ret)); + } } return ret; @@ -1428,8 +1569,8 @@ void hg_scanner_402::thread_handle_usb_read(void) ret = io_->read_interrupt(buf, &size); io_->set_timeout(1000); } - VLOG_MINI_4(LOG_LEVEL_DEBUG_INFO, "read-INT = %s: From(%d), Code(%d), Index(%d)\n", hg_scanner_err_name(ret), - info->From, info->Code, info->Img_Index); + VLOG_MINI_5(LOG_LEVEL_DEBUG_INFO, "read-INT = %s: From(%d), Code(%d), Index(%d), size = %d\n", hg_scanner_err_name(ret), + info->From, info->Code, info->Img_Index, size); if (ret != SCANNER_ERR_OK) { if (ret == SCANNER_ERR_TIMEOUT) @@ -1437,11 +1578,13 @@ void hg_scanner_402::thread_handle_usb_read(void) if (to_cnt++ < 30) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); - if ((get_status() & 0x03) == 0) - { - status_ = SCANNER_ERR_OK; - break; - } + //int statu = get_status(); + //if ((statu & 0x03) == 0) + //{ + // VLOG_MINI_1(LOG_LEVEL_FATAL, "USB thread over with status: 0x%x\n", statu); + // status_ = SCANNER_ERR_OK; + // break; + //} continue; } } @@ -1459,6 +1602,10 @@ void hg_scanner_402::thread_handle_usb_read(void) { read_one_image_from_usb(); } + else + { + VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "receive IMG event but no image count(%d)!", get_image_count()); + } break; case V4L2: VLOG_MINI_1(LOG_LEVEL_FATAL, "V4L2 error: %d\n", info->Code); @@ -1469,9 +1616,35 @@ void hg_scanner_402::thread_handle_usb_read(void) go = false; } break; + case STOPSCAN: + go = false; + LOG_INFO(LOG_LEVEL_ALL, "Received STOPSCAN event ...\n"); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + { + std::lock_guard lock(io_lock_); + + size = sizeof(buf); + ret = io_->read_interrupt(buf, &size); + io_->set_timeout(1000); + if (ret == SCANNER_ERR_OK && info->From == IMG) + { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + if (get_image_count() > 0) + { + read_one_image_from_usb(); + } + else + { + VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "receive IMG event but no image count(%d) after STOPSCAN!", get_image_count()); + } + } + } + break; default: break; } + if (!go) + break; } else VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "read %d bytes, sizeof(buf) = %d\n", size, sizeof(buf)); @@ -1502,6 +1675,7 @@ int hg_scanner_402::start(void) get_roller_num(); notify_ui_working_status(STATU_DESC_REWRITE_CONFIGURATION); writedown_device_configuration(); + writedown_image_configuration(); ret = read_register(/*SR_GET_ANDROID_STATE*/0x1001, &val); if (val) { @@ -1514,6 +1688,7 @@ int hg_scanner_402::start(void) else ret = status_ = SCANNER_ERR_IO; + VLOG_MINI_1(LOG_LEVEL_WARNING, "SR_GET_ANDROID_STATE result: %s\n", hg_scanner_err_name(ret)); return ret; } write_register(0x1000, scan_count_); diff --git a/hgdriver/hgdev/image_process.cpp b/hgdriver/hgdev/image_process.cpp index 4cbe7f9..2f4d929 100644 --- a/hgdriver/hgdev/image_process.cpp +++ b/hgdriver/hgdev/image_process.cpp @@ -5,6 +5,8 @@ #include #ifndef WIN32 #include +#pragma pack(push) +#pragma pack(1) typedef struct BITMAPFILEHEADER { u_int16_t bfType; @@ -28,6 +30,11 @@ typedef struct BITMAPINFOHEADER u_int32_t biClrUsed; u_int32_t biClrImportant; }BITMAPINFODEADER; +#pragma pack(pop) +#define BI_RGB 0 +#define MAKEWORD(a, b) (((a) & 0x0ff) | (((b) & 0x0ff) << 8)) +#define MAKELONG(a, b) (((a) & 0x0ffff) | (((b) & 0x0ffff) << 16)) +#define _countof(a) (sizeof(a) / sizeof((a)[0])) #else #include #include @@ -611,7 +618,7 @@ namespace hg_imgproc #endif return ret; } - //除网�? + //除网? int textureRemove() { int ret = SCANNER_ERR_OK; @@ -701,7 +708,7 @@ namespace hg_imgproc } return ret; } - //答题卡出�? + //答题卡出? int answerSheetFilterRed() { int ret = SCANNER_ERR_OK; @@ -1199,7 +1206,76 @@ namespace hg_imgproc } return SCANNER_ERR_OK; } + int save_2_bmp_file(const char* bmp_file, LPIMGHEAD head, void* buf, int resolution) + { + BITMAPINFOHEADER bih = { 0 }; + BITMAPFILEHEADER fh = { 0 }; + int pal_size = 0, line_len = (head->channels * head->bits * head->width + 31) / 32 * 4; + FILE *dst = fopen(bmp_file, "wb"); + if (!dst) + return errno; + + bih.biSize = sizeof(bih); + bih.biWidth = head->width; + bih.biBitCount = head->channels * head->bits; + bih.biSizeImage = head->height * line_len; + bih.biPlanes = 1; + bih.biHeight = head->height; + bih.biCompression = BI_RGB; + bih.biXPelsPerMeter = bih.biYPelsPerMeter = resolution * 39.37f + .5f; + + if (bih.biBitCount == 1) + pal_size = 2 * sizeof(int); + else if (bih.biBitCount == 8) + pal_size = 256 * sizeof(int); + + fh.bfType = MAKEWORD('B', 'M'); + fh.bfSize = sizeof(fh) + bih.biSizeImage + sizeof(bih); + fh.bfOffBits = sizeof(fh) + sizeof(bih) + pal_size; + + fwrite(&fh, sizeof(fh), 1, dst); + fwrite(&bih, sizeof(bih), 1, dst); + if (bih.biBitCount == 1) + { + int pal[] = { 0, 0x0ffffff }; + fwrite(pal, sizeof(pal), 1, dst); + } + 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)); + } + fwrite(g_bmp8_pallete, sizeof(g_bmp8_pallete), 1, dst); + } + + if (line_len == head->line_bytes) + fwrite(buf, 1, head->total_bytes, dst); + else + { + unsigned char* ptr = (unsigned char*)buf; + unsigned int pad = 0; + int pad_l = 4 - (head->line_bytes % 4), step = head->line_bytes; + + if (1) + { + ptr += head->total_bytes - head->line_bytes; + step *= -1; + } + for (int i = 0; i < head->height; ++i) + { + fwrite(ptr, head->line_bytes, 1, dst); + fwrite(&pad, 1, pad_l, dst); + ptr += step; + } + } + fclose(dst); + + return 0; + } } diff --git a/hgdriver/hgdev/image_process.h b/hgdriver/hgdev/image_process.h index a41b050..5440a0b 100644 --- a/hgdriver/hgdev/image_process.h +++ b/hgdriver/hgdev/image_process.h @@ -214,4 +214,5 @@ namespace hg_imgproc // seperate utilites ... int convert_image_file(SANE_ImageFormatConvert* conv); + int save_2_bmp_file(const char* bmp_file, LPIMGHEAD head, void* buf, int resolution); }