summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BUILD.gn29
-rw-r--r--samples/BUILD.gn29
-rw-r--r--samples/DEPS4
-rw-r--r--samples/pdfium_test.cc12
-rw-r--r--testing/image_diff/DEPS5
-rw-r--r--testing/image_diff/image_diff.cpp (renamed from samples/image_diff.cc)56
-rw-r--r--testing/image_diff/image_diff_png.cpp (renamed from samples/image_diff_png.cc)174
-rw-r--r--testing/image_diff/image_diff_png.h (renamed from samples/image_diff_png.h)6
8 files changed, 157 insertions, 158 deletions
diff --git a/BUILD.gn b/BUILD.gn
index 2c038b29c8..f577986f8e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -260,6 +260,18 @@ static_library("test_support") {
configs += [ ":pdfium_core_config" ]
}
+static_library("image_diff") {
+ testonly = true
+ sources = [
+ "testing/image_diff/image_diff_png.cpp",
+ "testing/image_diff/image_diff_png.h",
+ ]
+ deps = []
+ if (!pdf_enable_xfa) {
+ deps += [ "third_party:fx_lpng" ]
+ }
+}
+
# Targets below this are only visible within this file (and to the
# top-level gn_visibility target used to help gn_all build everything).
visibility = [
@@ -2045,6 +2057,22 @@ if (pdf_is_standalone) {
"//samples",
]
}
+
+ executable("pdfium_diff") {
+ testonly = true
+ sources = [
+ "testing/image_diff/image_diff.cpp",
+ ]
+ deps = [
+ ":image_diff",
+ ":pdfium",
+ "//build/config:exe_and_shlib_deps",
+ "//build/win:default_exe_manifest",
+ ]
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [ "//build/config/compiler:no_chromium_code" ]
+ }
+
group("fuzzers") {
testonly = true
deps = [
@@ -2062,6 +2090,7 @@ group("pdfium_all") {
if (pdf_is_standalone) {
deps += [
":fuzzers",
+ ":pdfium_diff",
":samples",
]
}
diff --git a/samples/BUILD.gn b/samples/BUILD.gn
index 779d4f3f31..59ab4b4a11 100644
--- a/samples/BUILD.gn
+++ b/samples/BUILD.gn
@@ -8,7 +8,6 @@ import("../pdfium.gni")
group("samples") {
testonly = true
deps = [
- ":pdfium_diff",
":pdfium_test",
]
}
@@ -46,19 +45,15 @@ config("pdfium_samples_config") {
executable("pdfium_test") {
testonly = true
sources = [
- "image_diff_png.cc",
- "image_diff_png.h",
"pdfium_test.cc",
]
deps = [
+ "../:image_diff",
"../:pdfium",
"../:test_support",
"//build/config:exe_and_shlib_deps",
"//build/win:default_exe_manifest",
]
- if (!pdf_enable_xfa) {
- deps += [ "../third_party:fx_lpng" ]
- }
if (pdf_enable_v8) {
deps += [ "//v8:v8_libplatform" ]
include_dirs = [
@@ -72,25 +67,3 @@ executable("pdfium_test") {
}
configs += [ ":pdfium_samples_config" ]
}
-
-executable("pdfium_diff") {
- testonly = true
- sources = [
- "image_diff.cc",
- "image_diff_png.cc",
- "image_diff_png.h",
- ]
- deps = [
- "../:pdfium",
- "//build/config:exe_and_shlib_deps",
- "//build/win:default_exe_manifest",
- ]
- if (!pdf_enable_xfa) {
- deps += [ "../third_party:fx_lpng" ]
- }
- configs -= [ "//build/config/compiler:chromium_code" ]
- configs += [
- ":pdfium_samples_config",
- "//build/config/compiler:no_chromium_code",
- ]
-}
diff --git a/samples/DEPS b/samples/DEPS
index 0a426d6e9c..26f9ee22eb 100644
--- a/samples/DEPS
+++ b/samples/DEPS
@@ -1,9 +1,5 @@
include_rules = [
'+public',
- '+third_party/libpng16',
'+third_party/skia/include',
- '+third_party/zlib',
'+v8',
- '+core/fdrm/crypto/fx_crypt.h',
- '+core/fxcrt/fx_memory.h',
]
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc
index a53edb77d3..ccd57a7e6a 100644
--- a/samples/pdfium_test.cc
+++ b/samples/pdfium_test.cc
@@ -18,7 +18,6 @@
#define _SKIA_SUPPORT_
#endif
-#include "core/fdrm/crypto/fx_crypt.h"
#include "public/cpp/fpdf_deleters.h"
#include "public/fpdf_annot.h"
#include "public/fpdf_dataavail.h"
@@ -28,7 +27,7 @@
#include "public/fpdf_structtree.h"
#include "public/fpdf_text.h"
#include "public/fpdfview.h"
-#include "samples/image_diff_png.h"
+#include "testing/image_diff/image_diff_png.h"
#include "testing/test_support.h"
#include "third_party/base/logging.h"
@@ -121,12 +120,9 @@ static bool CheckDimensions(int stride, int width, int height) {
static void OutputMD5Hash(const char* file_name, const char* buffer, int len) {
// Get the MD5 hash and write it to stdout.
- uint8_t digest[16];
- CRYPT_MD5Generate(reinterpret_cast<const uint8_t*>(buffer), len, digest);
- printf("MD5:%s:", file_name);
- for (int i = 0; i < 16; i++)
- printf("%02x", digest[i]);
- printf("\n");
+ std::string hash =
+ GenerateMD5Base16(reinterpret_cast<const uint8_t*>(buffer), len);
+ printf("MD5:%s:%s\n", file_name, hash.c_str());
}
static std::string WritePpm(const char* pdf_name,
diff --git a/testing/image_diff/DEPS b/testing/image_diff/DEPS
new file mode 100644
index 0000000000..4bd2335cd7
--- /dev/null
+++ b/testing/image_diff/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ '+third_party/libpng16',
+ '+third_party/zlib',
+]
+
diff --git a/samples/image_diff.cc b/testing/image_diff/image_diff.cpp
index 21460f5a62..806e0c84d5 100644
--- a/samples/image_diff.cc
+++ b/testing/image_diff/image_diff.cpp
@@ -20,7 +20,7 @@
#include <vector>
#include "core/fxcrt/fx_memory.h"
-#include "samples/image_diff_png.h"
+#include "testing/image_diff/image_diff_png.h"
#include "third_party/base/logging.h"
#include "third_party/base/numerics/safe_conversions.h"
@@ -39,30 +39,13 @@ static const uint32_t RGBA_ALPHA = 0xff000000;
class Image {
public:
- Image() : w_(0), h_(0) {
- }
-
- Image(const Image& image)
- : w_(image.w_),
- h_(image.h_),
- data_(image.data_) {
- }
-
- bool has_image() const {
- return w_ > 0 && h_ > 0;
- }
+ Image() : w_(0), h_(0) {}
+ Image(const Image& image) : w_(image.w_), h_(image.h_), data_(image.data_) {}
- int w() const {
- return w_;
- }
-
- int h() const {
- return h_;
- }
-
- const unsigned char* data() const {
- return &data_.front();
- }
+ bool has_image() const { return w_ > 0 && h_ > 0; }
+ int w() const { return w_; }
+ int h() const { return h_; }
+ const unsigned char* data() const { return &data_.front(); }
// Creates the image from the given filename on disk, and returns true on
// success.
@@ -202,15 +185,16 @@ float HistogramPercentageDifferent(const Image& baseline, const Image& actual) {
}
void PrintHelp() {
- fprintf(stderr,
- "Usage:\n"
- " image_diff [--histogram] <compare file> <reference file>\n"
- " Compares two files on disk, returning 0 when they are the same;\n"
- " passing \"--histogram\" additionally calculates a diff of the\n"
- " RGBA value histograms (which is resistant to shifts in layout)\n"
- " image_diff --diff <compare file> <reference file> <output file>\n"
- " Compares two files on disk, outputs an image that visualizes the\n"
- " difference to <output file>\n");
+ fprintf(
+ stderr,
+ "Usage:\n"
+ " image_diff [--histogram] <compare file> <reference file>\n"
+ " Compares two files on disk, returning 0 when they are the same;\n"
+ " passing \"--histogram\" additionally calculates a diff of the\n"
+ " RGBA value histograms (which is resistant to shifts in layout)\n"
+ " image_diff --diff <compare file> <reference file> <output file>\n"
+ " Compares two files on disk, outputs an image that visualizes the\n"
+ " difference to <output file>\n");
}
int CompareImages(const std::string& file1,
@@ -296,9 +280,9 @@ int DiffImages(const std::string& file1,
return kStatusSame;
std::vector<unsigned char> png_encoding;
- image_diff_png::EncodeRGBAPNG(
- diff_image.data(), diff_image.w(), diff_image.h(),
- diff_image.w() * 4, &png_encoding);
+ image_diff_png::EncodeRGBAPNG(diff_image.data(), diff_image.w(),
+ diff_image.h(), diff_image.w() * 4,
+ &png_encoding);
FILE* f = fopen(out_file.c_str(), "wb");
if (!f)
diff --git a/samples/image_diff_png.cc b/testing/image_diff/image_diff_png.cpp
index 3d12b7e948..a5e8cdb101 100644
--- a/samples/image_diff_png.cc
+++ b/testing/image_diff/image_diff_png.cpp
@@ -9,7 +9,7 @@
// This is a duplicate of ui/gfx/codec/png_codec.cc, after removing code related
// to Skia, that we can use when running layout tests with minimal dependencies.
-#include "samples/image_diff_png.h"
+#include "testing/image_diff/image_diff_png.h"
#include <stdlib.h>
#include <string.h>
@@ -44,8 +44,10 @@ struct Comment {
};
// Converts BGRA->RGBA and RGBA->BGRA.
-void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
- unsigned char* output, bool* is_opaque) {
+void ConvertBetweenBGRAandRGBA(const unsigned char* input,
+ int pixel_width,
+ unsigned char* output,
+ bool* is_opaque) {
for (int x = 0; x < pixel_width; x++) {
const unsigned char* pixel_in = &input[x * 4];
unsigned char* pixel_out = &output[x * 4];
@@ -56,8 +58,10 @@ void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
}
}
-void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
- unsigned char* rgb, bool* is_opaque) {
+void ConvertRGBAtoRGB(const unsigned char* rgba,
+ int pixel_width,
+ unsigned char* rgb,
+ bool* is_opaque) {
for (int x = 0; x < pixel_width; x++) {
const unsigned char* pixel_in = &rgba[x * 4];
unsigned char* pixel_out = &rgb[x * 3];
@@ -92,8 +96,7 @@ class PngDecoderState {
row_converter(NULL),
width(0),
height(0),
- done(false) {
- }
+ done(false) {}
ColorFormat output_format;
int output_channels;
@@ -107,7 +110,9 @@ class PngDecoderState {
// Called to convert a row from the library to the correct output format.
// When NULL, no conversion is necessary.
- void (*row_converter)(const unsigned char* in, int w, unsigned char* out,
+ void (*row_converter)(const unsigned char* in,
+ int w,
+ unsigned char* out,
bool* is_opaque);
// Size of the image, set in the info callback.
@@ -118,8 +123,10 @@ class PngDecoderState {
bool done;
};
-void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width,
- unsigned char* rgba, bool* is_opaque) {
+void ConvertRGBtoRGBA(const unsigned char* rgb,
+ int pixel_width,
+ unsigned char* rgba,
+ bool* is_opaque) {
for (int x = 0; x < pixel_width; x++) {
const unsigned char* pixel_in = &rgb[x * 3];
unsigned char* pixel_out = &rgba[x * 4];
@@ -130,8 +137,10 @@ void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width,
}
}
-void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width,
- unsigned char* bgra, bool* is_opaque) {
+void ConvertRGBtoBGRA(const unsigned char* rgb,
+ int pixel_width,
+ unsigned char* bgra,
+ bool* is_opaque) {
for (int x = 0; x < pixel_width; x++) {
const unsigned char* pixel_in = &rgb[x * 3];
unsigned char* pixel_out = &bgra[x * 4];
@@ -145,8 +154,8 @@ void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width,
// Called when the png header has been read. This code is based on the WebKit
// PNGImageDecoder
void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) {
- PngDecoderState* state = static_cast<PngDecoderState*>(
- png_get_progressive_ptr(png_ptr));
+ PngDecoderState* state =
+ static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
int bit_depth, color_type, interlace_type, compression_type;
int filter_type, channels;
@@ -246,14 +255,15 @@ void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) {
longjmp(png_jmpbuf(png_ptr), 1);
}
- state->output->resize(
- state->width * state->output_channels * state->height);
+ state->output->resize(state->width * state->output_channels * state->height);
}
-void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row,
- png_uint_32 row_num, int pass) {
- PngDecoderState* state = static_cast<PngDecoderState*>(
- png_get_progressive_ptr(png_ptr));
+void DecodeRowCallback(png_struct* png_ptr,
+ png_byte* new_row,
+ png_uint_32 row_num,
+ int pass) {
+ PngDecoderState* state =
+ static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
if (static_cast<int>(row_num) > state->height) {
NOTREACHED();
@@ -271,8 +281,8 @@ void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row,
}
void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
- PngDecoderState* state = static_cast<PngDecoderState*>(
- png_get_progressive_ptr(png_ptr));
+ PngDecoderState* state =
+ static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
// Mark the image as complete, this will tell the Decode function that we
// have successfully found the end of the data.
@@ -283,18 +293,18 @@ void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
// cleanup and error handling code cleaner.
class PngReadStructDestroyer {
public:
- PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
- }
- ~PngReadStructDestroyer() {
- png_destroy_read_struct(ps_, pi_, NULL);
- }
+ PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {}
+ ~PngReadStructDestroyer() { png_destroy_read_struct(ps_, pi_, NULL); }
+
private:
png_struct** ps_;
png_info** pi_;
};
-bool BuildPNGStruct(const unsigned char* input, size_t input_size,
- png_struct** png_ptr, png_info** info_ptr) {
+bool BuildPNGStruct(const unsigned char* input,
+ size_t input_size,
+ png_struct** png_ptr,
+ png_info** info_ptr) {
if (input_size < 8)
return false; // Input data too small to be a png
@@ -318,9 +328,12 @@ bool BuildPNGStruct(const unsigned char* input, size_t input_size,
} // namespace
// static
-bool Decode(const unsigned char* input, size_t input_size,
- ColorFormat format, std::vector<unsigned char>* output,
- int* w, int* h) {
+bool Decode(const unsigned char* input,
+ size_t input_size,
+ ColorFormat format,
+ std::vector<unsigned char>* output,
+ int* w,
+ int* h) {
png_struct* png_ptr = NULL;
png_info* info_ptr = NULL;
if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr))
@@ -338,9 +351,7 @@ bool Decode(const unsigned char* input, size_t input_size,
png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback,
&DecodeRowCallback, &DecodeEndCallback);
- png_process_data(png_ptr,
- info_ptr,
- const_cast<unsigned char*>(input),
+ png_process_data(png_ptr, info_ptr, const_cast<unsigned char*>(input),
input_size);
if (!state.done) {
@@ -382,8 +393,10 @@ void FakeFlushCallback(png_structp png) {
// we're required to provide this function by libpng.
}
-void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width,
- unsigned char* rgb, bool* is_opaque) {
+void ConvertBGRAtoRGB(const unsigned char* bgra,
+ int pixel_width,
+ unsigned char* rgb,
+ bool* is_opaque) {
for (int x = 0; x < pixel_width; x++) {
const unsigned char* pixel_in = &bgra[x * 4];
unsigned char* pixel_out = &rgb[x * 3];
@@ -406,8 +419,7 @@ inline char* strdup(const char* str) {
class CommentWriter {
public:
explicit CommentWriter(const std::vector<Comment>& comments)
- : comments_(comments),
- png_text_(new png_text[comments.size()]) {
+ : comments_(comments), png_text_(new png_text[comments.size()]) {
for (size_t i = 0; i < comments.size(); ++i)
AddComment(i, comments[i]);
}
@@ -417,20 +429,14 @@ class CommentWriter {
free(png_text_[i].key);
free(png_text_[i].text);
}
- delete [] png_text_;
+ delete[] png_text_;
}
- bool HasComments() {
- return !comments_.empty();
- }
+ bool HasComments() { return !comments_.empty(); }
- png_text* get_png_text() {
- return png_text_;
- }
+ png_text* get_png_text() { return png_text_; }
- int size() {
- return static_cast<int>(comments_.size());
- }
+ int size() { return static_cast<int>(comments_.size()); }
private:
void AddComment(size_t pos, const Comment& comment) {
@@ -454,18 +460,25 @@ class CommentWriter {
#endif // PNG_TEXT_SUPPORTED
// The type of functions usable for converting between pixel formats.
-typedef void (*FormatConverter)(const unsigned char* in, int w,
- unsigned char* out, bool* is_opaque);
+typedef void (*FormatConverter)(const unsigned char* in,
+ int w,
+ unsigned char* out,
+ bool* is_opaque);
// libpng uses a wacky setjmp-based API, which makes the compiler nervous.
// We constrain all of the calls we make to libpng where the setjmp() is in
// place to this function.
// Returns true on success.
-bool DoLibpngWrite(png_struct* png_ptr, png_info* info_ptr,
+bool DoLibpngWrite(png_struct* png_ptr,
+ png_info* info_ptr,
PngEncoderState* state,
- int width, int height, int row_byte_width,
- const unsigned char* input, int compression_level,
- int png_output_color_type, int output_color_components,
+ int width,
+ int height,
+ int row_byte_width,
+ const unsigned char* input,
+ int compression_level,
+ int png_output_color_type,
+ int output_color_components,
FormatConverter converter,
const std::vector<Comment>& comments) {
#ifdef PNG_TEXT_SUPPORTED
@@ -501,14 +514,14 @@ bool DoLibpngWrite(png_struct* png_ptr, png_info* info_ptr,
if (!converter) {
// No conversion needed, give the data directly to libpng.
- for (int y = 0; y < height; y ++) {
+ for (int y = 0; y < height; y++) {
png_write_row(png_ptr,
const_cast<unsigned char*>(&input[y * row_byte_width]));
}
} else {
// Needs conversion using a separate buffer.
row_buffer = new unsigned char[width * output_color_components];
- for (int y = 0; y < height; y ++) {
+ for (int y = 0; y < height; y++) {
converter(&input[y * row_byte_width], width, row_buffer, NULL);
png_write_row(png_ptr, row_buffer);
}
@@ -522,8 +535,10 @@ bool DoLibpngWrite(png_struct* png_ptr, png_info* info_ptr,
} // namespace
// static
-bool EncodeWithCompressionLevel(const unsigned char* input, ColorFormat format,
- const int width, const int height,
+bool EncodeWithCompressionLevel(const unsigned char* input,
+ ColorFormat format,
+ const int width,
+ const int height,
int row_byte_width,
bool discard_transparency,
const std::vector<Comment>& comments,
@@ -578,8 +593,8 @@ bool EncodeWithCompressionLevel(const unsigned char* input, ColorFormat format,
if (input_color_components * width < row_byte_width)
return false;
- png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
- NULL, NULL, NULL);
+ png_struct* png_ptr =
+ png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
return false;
png_info* info_ptr = png_create_info_struct(png_ptr);
@@ -589,32 +604,35 @@ bool EncodeWithCompressionLevel(const unsigned char* input, ColorFormat format,
}
PngEncoderState state(output);
- bool success = DoLibpngWrite(png_ptr, info_ptr, &state,
- width, height, row_byte_width,
- input, compression_level, png_output_color_type,
- output_color_components, converter, comments);
+ bool success =
+ DoLibpngWrite(png_ptr, info_ptr, &state, width, height, row_byte_width,
+ input, compression_level, png_output_color_type,
+ output_color_components, converter, comments);
png_destroy_write_struct(&png_ptr, &info_ptr);
return success;
}
// static
-bool Encode(const unsigned char* input, ColorFormat format,
- const int width, const int height, int row_byte_width,
+bool Encode(const unsigned char* input,
+ ColorFormat format,
+ const int width,
+ const int height,
+ int row_byte_width,
bool discard_transparency,
const std::vector<Comment>& comments,
std::vector<unsigned char>* output) {
return EncodeWithCompressionLevel(input, format, width, height,
- row_byte_width,
- discard_transparency,
- comments, Z_DEFAULT_COMPRESSION,
- output);
+ row_byte_width, discard_transparency,
+ comments, Z_DEFAULT_COMPRESSION, output);
}
// Decode a PNG into an RGBA pixel array.
-bool DecodePNG(const unsigned char* input, size_t input_size,
+bool DecodePNG(const unsigned char* input,
+ size_t input_size,
std::vector<unsigned char>* output,
- int* width, int* height) {
+ int* width,
+ int* height) {
return Decode(input, input_size, FORMAT_RGBA, output, width, height);
}
@@ -624,9 +642,8 @@ bool EncodeRGBAPNG(const unsigned char* input,
int height,
int row_byte_width,
std::vector<unsigned char>* output) {
- return Encode(input, FORMAT_RGBA,
- width, height, row_byte_width, false,
- std::vector<Comment>(), output);
+ return Encode(input, FORMAT_RGBA, width, height, row_byte_width, false,
+ std::vector<Comment>(), output);
}
// Encode an BGRA pixel array into a PNG.
@@ -636,9 +653,8 @@ bool EncodeBGRAPNG(const unsigned char* input,
int row_byte_width,
bool discard_transparency,
std::vector<unsigned char>* output) {
- return Encode(input, FORMAT_BGRA,
- width, height, row_byte_width, discard_transparency,
- std::vector<Comment>(), output);
+ return Encode(input, FORMAT_BGRA, width, height, row_byte_width,
+ discard_transparency, std::vector<Comment>(), output);
}
} // namespace image_diff_png
diff --git a/samples/image_diff_png.h b/testing/image_diff/image_diff_png.h
index 7bb395a4a5..4d87aa1cc0 100644
--- a/samples/image_diff_png.h
+++ b/testing/image_diff/image_diff_png.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef SAMPLES_IMAGE_DIFF_PNG_H_
-#define SAMPLES_IMAGE_DIFF_PNG_H_
+#ifndef TESTING_IMAGE_DIFF_IMAGE_DIFF_PNG_H_
+#define TESTING_IMAGE_DIFF_IMAGE_DIFF_PNG_H_
#include <stdlib.h> // for size_t.
@@ -35,4 +35,4 @@ bool EncodeBGRAPNG(const unsigned char* input,
} // namespace image_diff_png
-#endif // SAMPLES_IMAGE_DIFF_PNG_H_
+#endif // TESTING_IMAGE_DIFF_IMAGE_DIFF_PNG_H_